Question
Why Self-Closing Script Tags Don’t Work in HTML and XHTML
Question
In HTML, why do browsers not correctly recognize a self-closing script element like this?
<script src="foobar.js" />
But they do recognize this form:
<script src="foobar.js"></script>
What is the reason for this behavior? Does it conflict with the idea of XHTML support?
This behavior has been observed in browsers such as older versions of Internet Explorer.
Short Answer
By the end of this page, you will understand why script tags require a separate closing tag in normal HTML, why /> does not make a script element self-closing in HTML parsing, and how XHTML behaves differently only when the document is truly served as XHTML. You will also learn practical rules for writing script tags safely across browsers.
Concept
The core idea
The issue is not really about JavaScript. It is about how browsers parse HTML.
In HTML, the <script> element is not a void element. That means it is expected to have both:
- an opening tag
- a closing tag
So this is valid HTML:
<script src="foobar.js"></script>
But this is not treated as a self-closing script element in HTML:
<script src="foobar.js" />
Why /> does not work here
In HTML syntax, the slash in /> is not a universal "self-close this tag" instruction for every element. HTML only allows certain elements to omit closing tags because they are void elements, such as:
<br><img><input><meta><link>
<script> is not one of them.
So when an HTML parser sees:
<script src="foobar.js" />
it does not interpret that as "open and immediately close the script tag". Instead, browsers generally treat it as a start tag for script, and the element remains open until a real </script> is found.
Why script is special
The script element can contain inline JavaScript:
<script>
console.log("Hello");
</script>
Because of that, HTML parsing rules treat script as an element with raw text-like contents. The parser expects an explicit closing </script> tag to know where the script ends.
What about XHTML?
This does not break XHTML support.
In true XHTML, which is XML, self-closing syntax is meaningful:
<script src="foobar.js" />
However, there is an important catch:
- If the document is parsed as HTML, HTML parsing rules apply.
- If the document is parsed as XML/XHTML, XML parsing rules apply.
Historically, many pages were written to look like XHTML but were actually served to browsers as text/html. In that case, browsers did not use XML parsing. They used HTML parsing, so self-closing script still failed.
Why this matters in real programming
If you write HTML that mixes XML-style syntax with HTML parsing assumptions, you can create subtle browser bugs. Knowing how the browser reads markup helps you write code that works consistently.
The safest rule is simple:
<script src="app.js"></script>
Always use an explicit closing tag for script in HTML documents.
Mental Model
Think of the HTML parser like a reader following a grammar book.
- Some tags are like single-word commands:
<br>or<img> - Some tags are like paired brackets:
<script> ... </script>
<script> belongs to the second group.
Writing:
<script src="foobar.js" />
in HTML is like opening a bracket and hoping the reader will guess it closes itself. But the grammar rules for HTML do not allow that for script.
In XML/XHTML, the grammar is stricter and /> has a real self-closing meaning. But only if the browser is actually reading the document as XML, not as ordinary HTML.
So the key mental model is:
- HTML mode:
scriptneeds</script> - XML/XHTML mode:
/>can self-close, but only under XML parsing rules
Syntax and Examples
Correct HTML syntax
Use a normal opening and closing script tag:
<script src="foobar.js"></script>
Incorrect for normal HTML parsing
<script src="foobar.js" />
In HTML, this does not reliably self-close the element.
Inline script example
<script>
const message = "Hello";
console.log(message);
</script>
This works because the parser clearly sees where the script starts and ends.
External script example
<!doctype html>
<html>
<head>
<script src="app.js"></script>
</head>
<body>
<h1>Page loaded</h1>
</body>
</html>
The browser loads app.js and executes it when it reaches the tag, according to normal script-loading rules.
XHTML note
If a page is truly served as XHTML/XML, this may be valid XML syntax:
<script src="app.js" />
But this is not a safe general HTML pattern. In modern web development, when writing HTML documents, always close script explicitly.
Step by Step Execution
Example to trace
Consider this HTML:
<!doctype html>
<html>
<body>
<script src="app.js" />
<p>Hello</p>
</body>
</html>
What the HTML parser does
Step 1: Reads <script src="app.js" />
Because the document is being parsed as HTML, the parser sees a script start tag.
Step 2: Ignores the idea of self-closing for script
The trailing / does not make script a valid self-closing HTML element.
Step 3: Keeps the script element open
The parser expects a real closing tag:
</script>
Step 4: Continues reading the document as part of script handling rules
This can cause the following markup to be interpreted incorrectly until the parser finds </script>.
Step 5: Page structure may break
Elements like:
<p>Hello</p>
may not be parsed the way you expect.
Correct version
<!doctype html>
<html>
<body>
<script src="app.js"></script>
<p>Hello</p>
</body>
</html>
Now the parser sees:
- opening
script - script source attribute
- closing
</script> - normal continuation of the HTML document
Real World Use Cases
Where this matters
Including JavaScript files in web pages
This is the most direct use case:
<script src="main.js"></script>
If you use self-closing syntax in HTML, the browser may parse the page incorrectly.
Server-rendered templates
In template engines such as EJS, Handlebars, Twig, or JSP, developers often generate script tags dynamically. Using proper closing tags avoids cross-browser parsing bugs.
CMS platforms
Systems like WordPress, Drupal, or custom CMS tools inject scripts into pages. Incorrect markup can break analytics, widgets, or app initialization.
Embedded third-party tools
Chat widgets, tracking scripts, ad code, and payment integrations are often inserted with script tags. These snippets must use standard HTML syntax to work reliably.
Legacy browser support
Older browsers were especially sensitive to parser differences. Explicit closing tags were necessary for compatibility.
Real Codebase Usage
How developers use this in real projects
Standard script inclusion
Developers almost always write:
<script src="bundle.js"></script>
This is the normal, compatible pattern.
Template-generated assets
In production code, asset helpers often generate script tags:
<script src="/static/app.js"></script>
Frameworks and build tools output full opening and closing tags because that is the correct HTML form.
Guarding against parsing bugs
Teams avoid XHTML-style self-closing syntax in HTML templates because it can silently break document structure.
Linting and formatting
HTML formatters and linters usually preserve script with explicit closing tags. This helps keep markup standards-compliant and consistent.
Component and SSR environments
Even in modern component systems, rendered HTML output still follows HTML parsing rules. So although JSX may look XML-like, the generated HTML for a script element still needs proper structure.
Example in JSX:
<script src="/app.js"></script>
That stays aligned with browser expectations.
Common Mistakes
1. Assuming /> self-closes every HTML tag
Broken example:
<script src="app.js" />
Why it is wrong:
scriptis not a void element in HTML- the parser still expects
</script>
Fix:
<script src="app.js"></script>
2. Confusing XHTML-looking syntax with actual XHTML parsing
Beginners often think that writing HTML like this means the browser will parse it as XML:
<!doctype html>
<html>
<head>
<script src="app.js" />
</head>
</html>
It does not. If the page is served as text/html, it is parsed as HTML.
3. Assuming browser support means parser rules are the same everywhere
Older browsers exposed the problem more obviously, but the underlying rule is not just an old-IE issue. It comes from HTML parsing behavior itself.
4. Forgetting that script can contain inline code
Because script may contain content, the browser needs a clear end marker.
Correct:
<script>
alert("Hi");
</script>
Not valid as HTML self-closing syntax:
<script />
5. Mixing XML habits into HTML files
Developers coming from XML may write self-closing tags everywhere. In HTML, only specific elements are void.
A good rule:
- Use self-closing style only where HTML allows omission of closing tags
- Always explicitly close
scriptandstyle
Comparisons
HTML vs XHTML parsing
| Topic | HTML | XHTML/XML |
|---|---|---|
| Parsing model | HTML parser | XML parser |
Meaning of /> | Not a universal self-close for all tags | Self-closing syntax is meaningful |
script tag | Requires </script> | Can be self-closed syntactically if valid in XML context |
| Common content type | text/html | application/xhtml+xml |
| Browser behavior | Follows HTML rules | Follows XML rules |
Void elements vs normal elements
| Element type | Example | Closing tag needed? |
|---|---|---|
| Void element | <img> | No |
| Void element | <br> | No |
| Normal element | <script> | Yes |
| Normal element | <div> | Yes |
| Normal element | <p> | Yes, though HTML may auto-close in some cases |
script vs img
<img src="photo.png" />
<script src="app.js" />
These may look similar, but they are not equivalent.
imgis a void elementscriptis not
That is why the first works and the second is unreliable in HTML.
Cheat Sheet
Quick rules
- In HTML, always write external scripts like this:
<script src="app.js"></script>
- Do not rely on this in HTML:
<script src="app.js" />
scriptis not a void element.img,br,input,meta, andlinkare void elements.- XHTML rules only apply when the document is actually parsed as XML/XHTML.
- A page served as
text/htmluses HTML parsing rules, even if the markup looks XML-like.
Safe default
If you are writing web pages for browsers, use:
<script></script>
or
<script src="file.js"></script>
Remember
- HTML syntax and XML syntax are different
- browser parsing mode matters
/>does not magically self-close every tag in HTML
FAQ
Why does <script src="file.js" /> fail in HTML?
Because HTML does not treat script as a self-closing element. The parser expects a real </script> closing tag.
Is this only a problem in old Internet Explorer?
No. Older IE made it easier to notice, but the underlying issue comes from HTML parsing rules.
Does XHTML allow self-closing script tags?
In true XML/XHTML parsing, /> has self-closing meaning. But that only applies if the document is actually served and parsed as XHTML/XML.
If my document looks like XHTML, is that enough?
No. What matters is how the browser parses it. If it is served as text/html, HTML rules apply.
Why is img allowed to self-close but script is not?
Because img is a void element in HTML, while script is a normal element that requires a closing tag.
Should I always close script tags explicitly?
Yes. In normal HTML documents, that is the correct and safe approach.
Does this apply to inline scripts too?
Yes. Inline scripts also require an explicit closing </script> tag in HTML.
Mini Project
Description
Build a small HTML page that loads an external JavaScript file correctly and demonstrates why proper script tag syntax matters. This project helps you practice using valid HTML script inclusion and observing browser behavior safely.
Goal
Create a page that loads an external script and updates the page content using the correct script tag syntax.
Requirements
Create an index.html file with a heading and an empty paragraph element.
Create an app.js file that changes the paragraph text when the script runs.
Include app.js in index.html using a correctly closed script tag.
Open the page in a browser and confirm that the text updates.
Optionally test the broken self-closing form and observe that it is unreliable.
Keep learning
Related questions
Add a Favicon to a Static HTML Page
Learn how to add a favicon to static HTML pages, use correct paths and icon formats, and troubleshoot browser caching and local-file issues.
Allow Only Numeric Input (0-9) in HTML Input Using jQuery
Learn how to allow only digits 0-9 in an HTML input using jQuery, with examples, validation tips, common mistakes, and best practices.
Apply CSS !important with jQuery .css()
Learn why jQuery .css() cannot set !important and how to use CSSStyleDeclaration.setProperty() to apply inline important styles safely.