In an HTML page, what actually changes if you move <script src="app.js"> from just before </body> into <head> and add the defer attribute?
answer
- same guarantee, two different mechanisms
- when the parser first sees the tag
- late discovery costs a round trip
- one placement still blocks what follows
basics
~20 sThe download starts earlier, because the parser discovers the tag at the top instead of at the bottom. Execution timing barely moves: both forms run after the markup exists and before DOMContentLoaded, but the deferred one waits for the complete document.
solid answer
~50 sBoth placements avoid the classic problem of code running before its elements exist, but they differ in *discovery*. A tag before `</body>` is only seen once the parser has walked the whole document, so the fetch starts late; a `defer` tag in `<head>` is seen immediately, so the file downloads in parallel with parsing and is usually ready the moment parsing ends. Execution timing is close but not identical: the body-end script runs the instant the parser reaches it, which is before any markup that follows it is parsed, while a deferred script runs only after the entire document has been parsed. Both run before `DOMContentLoaded`. There is one more difference in kind: a body-end script with no attribute is still parser-blocking, so anything after it — including a footer or a trailing element — waits for it to download and execute. `defer` in `<head>` is the better default for first-party code today; end-of-body placement is a habit from before `defer` was universally supported.
go deeper
Know that both placements let your code find the elements it needs, and that putting the tag in head with defer is the modern default while end-of-body is the older habit.
Explain discovery: the parser must reach a tag before it can fetch it, so a bottom-of-body script downloads late. Note that a deferred script waits for the complete document, while a body-end one runs the moment the parser reaches it.
Argue it as a robustness point, not just speed: defer encodes the DOM-complete guarantee in markup, so a teammate appending markup below the old tag position cannot silently break the assumption. Note the execution cost is unchanged either way.
Own the template: decide where scripts may live in the shared page shell, standardise on deferred head tags for first-party code, and make sure legacy bottom-of-body snippets are inventoried rather than left as unexplained exceptions.
## Two ways to get the same guarantee The reason people put scripts before `</body>` is a real one: a classic script with no loading attribute blocks the parser, and code that runs before the elements it touches have been parsed throws `null` errors. Putting the tag last means there is nothing left to block and nothing left to be missing. `defer` solves the same two problems differently — by keeping the tag where the browser finds it first, and postponing only the execution. ## Discovery is the real difference The HTML parser cannot fetch a resource it has not read. A `<script>` tag at the end of a long document is discovered after the parser has tokenised every byte above it, so the network request begins late. A `defer` tag in `<head>` is discovered in the first kilobytes, so the request overlaps with the parse and with fetching everything else. ```html <head> <script defer src="/app.js"></script> <!-- fetch starts now --> </head> <body> <!-- ...the whole page... --> <script src="/app.js"></script> <!-- fetch starts here instead --> </body> ``` On a fast connection and a short page the gap is small. On a slow connection and a long document it is the difference between the script being ready when parsing ends and the user waiting for a fresh round trip afterwards. ## Execution timing is close, but not the same A body-end classic script executes at the point the parser reaches it. If any markup follows the tag — even a stray closing structure, an inline snippet, or a footer someone adds later — that markup is not in the DOM yet when the code runs. A deferred script executes only after the *complete* document has been parsed, so it can never see a partial DOM. Both run before `DOMContentLoaded`. That makes `defer` strictly safer against the future edit. "It works because nothing comes after it" is a guarantee held in place by convention, not by the platform; `defer` states the guarantee in markup. ## The blocking that remains at the bottom A body-end tag without `defer` is still a parser-blocking script. Everything below it, and the completion of parsing itself, waits for the download and the execution. Since `DOMContentLoaded` fires only after parsing completes, a slow bottom-of-body script delays that event exactly as a head one would — it just delays less content on the way. ## Ordering Within each style, source order is preserved: consecutive body-end scripts run in order, and consecutive deferred scripts run in order. Mixing them is where reasoning gets awkward, because a deferred head script runs after a non-deferred body-end script even though it appears first in the document — the deferred queue drains at end of parse, and the body-end script already ran during the parse. ## What does not change Moving the tag does not make the script cheaper. It still executes on the main thread and still blocks rendering while it does. If the bundle takes 400 ms to evaluate, that cost is paid in both placements — earlier discovery just means it is paid at a more predictable moment, not that it disappears. ## The practical rule Put first-party scripts in `<head>` with `defer`. Keep an eye out for the legacy pattern in old templates — a bare `<script src>` before `</body>` — and treat converting it as a safe, mechanical improvement: it starts the download sooner and hardens the "DOM is ready" assumption against anyone appending markup below.
- If both placements run the code before DOMContentLoaded, why does the head-with-defer version usually feel faster?Because the fetch starts earlier. The parser must reach a tag before it can request it, so a bottom-of-body script's download begins only after the whole document has been tokenised. With `defer` in `<head>` the request overlaps parsing, so the file is often already in memory when parsing ends and execution starts immediately instead of after a round trip.
- Is there any case where a bare script before </body> is still the right call?Rarely. The honest cases are code that must run synchronously against the markup above it and deliberately before the rest of the page parses, or a snippet a vendor supplies that way and that you cannot modify. For everything you control, `defer` in `<head>` gives the same DOM guarantee with an earlier fetch and no dependence on nothing being added below.
saying these in an interview costs you the question
- Says the two placements are exactly equivalent
- Claims a bottom-of-body script blocks nothing
- Thinks defer delays DOMContentLoaded until after load
- Says moving the tag reduces script execution cost
- Believes end-of-body placement is still best practice