Question

Why Self-Closing Script Tags Don’t Work in HTML and XHTML

htmljavascriptinternet-explorerxhtml

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: script needs </script>
  • XML/XHTML mode: /> can self-close, but only under XML parsing rules
Take Quiz

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:

  1. opening script
  2. script source attribute
  3. closing </script>
  4. 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.

Take Quiz

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:

  • script is 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 script and style

Comparisons

HTML vs XHTML parsing

TopicHTMLXHTML/XML
Parsing modelHTML parserXML parser
Meaning of />Not a universal self-close for all tagsSelf-closing syntax is meaningful
script tagRequires </script>Can be self-closed syntactically if valid in XML context
Common content typetext/htmlapplication/xhtml+xml
Browser behaviorFollows HTML rulesFollows XML rules

Void elements vs normal elements

Element typeExampleClosing 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.

  • img is a void element
  • script is not

That is why the first works and the second is unreliable in HTML.

Take Quiz

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" />
  • script is not a void element.
  • img, br, input, meta, and link are void elements.
  • XHTML rules only apply when the document is actually parsed as XML/XHTML.
  • A page served as text/html uses 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.

Take Quiz

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.

Take Quiz

Keep learning

Related questions

View more