# Element “script” must not have attribute “defer” unless attribute “src” is also specified.

> Canonical HTML version: https://rocketvalidator.com/html-validation/element-script-must-not-have-attribute-defer-unless-attribute-src-is-also-specified
> Attribution: Rocket Validator (https://rocketvalidator.com)
> License: CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/)

The `defer` and `async` boolean attributes control how and when an external script is fetched and executed relative to HTML parsing. These attributes exist specifically to optimize the loading of external resources. An inline `<script>` block (one without a `src` attribute) doesn't need to be "downloaded" — its content is already embedded in the HTML document. Because of this, the `defer` attribute has no meaningful effect on inline scripts, and the HTML specification explicitly forbids this combination.

According to the [WHATWG HTML living standard](https://html.spec.whatwg.org/multipage/scripting.html#attr-script-defer), the `defer` attribute "must not be specified if the `src` attribute is not present." Browsers will simply ignore the `defer` attribute on inline scripts, which means the script will execute synchronously as if `defer` were never added. This can mislead developers into thinking their inline script execution is being deferred when it isn't, potentially causing subtle timing bugs that are difficult to diagnose.

The same rule applies to the `async` attribute — it also requires the presence of a `src` attribute to be valid.

## How to fix it

You have two options depending on your situation:

1. **If the script should be deferred**, move the inline code into an external `.js` file and reference it with the `src` attribute alongside `defer`.
2. **If the script must remain inline**, remove the `defer` attribute entirely. If you need deferred execution for inline code, consider placing the `<script>` element at the end of the `<body>`, or use `DOMContentLoaded` to wait for the document to finish parsing.

## Examples

### ❌ Invalid: `defer` on an inline script

```html
<script defer>
  console.log("hello");
</script>
```

This triggers the validation error because `defer` is present without a corresponding `src` attribute.

### ✅ Fix option 1: Add a `src` attribute

Move the JavaScript into an external file (e.g., `app.js`) and reference it:

```html
<script defer src="app.js"></script>
```

The browser will download `app.js` in parallel with HTML parsing and execute it only after the document is fully parsed.

### ✅ Fix option 2: Remove `defer` from the inline script

If the script must stay inline, simply remove the `defer` attribute:

```html
<script>
  console.log("hello");
</script>
```

### ✅ Fix option 3: Use `DOMContentLoaded` for deferred inline execution

If you need your inline script to wait until the DOM is ready, wrap the code in a `DOMContentLoaded` event listener:

```html
<script>
  document.addEventListener("DOMContentLoaded", function() {
    console.log("DOM is fully parsed");
  });
</script>
```

This achieves a similar effect to `defer` but is valid for inline scripts.

### ❌ Invalid: `async` on an inline script

The same rule applies to `async`:

```html
<script async>
  document.title = "Updated";
</script>
```

### ✅ Fixed

```html
<script async src="update-title.js"></script>
```

Or simply remove `async` if the script is inline:

```html
<script>
  document.title = "Updated";
</script>
```
