# Move CSS to head (move_css_to_head)

Moves CSS link into head. How the mod_pagespeed move_css_to_head filter works, when to use it, its risks and the Apache, nginx and IIS directives.

Canonical URL: https://modpagespeed.com/docs/filters/move_css_to_head/

- Category: CSS
- CoreFilters: No
- OptimizeForBandwidth: No
- Risk: Generally safe

`move_css_to_head` collects stylesheet references that sit in the `<body>` and appends them to the end of `<head>`. Stylesheets discovered in the body still apply, but the browser finds them late in the parse, and a late stylesheet can repaint content that was already shown without it. Gathering them into `<head>` puts every download where the browser expects stylesheets and starts them all at once.

## How it works

Not a core filter; enable it by name. Live demo: [move_css_to_head](https://modpagespeed.com/examples/move_css_to_head/).

### When it helps and when it does not

It helps on pages whose markup carries `<link>` or `<style>` elements inside the body, which is typical of older templates and CMS output that renders per-section stylesheets where the section appears. On pages that already keep their CSS in `<head>` there is nothing to move and the filter is a no-op. The filter changes where stylesheets load from, not their order relative to each other, so the cascade survives; the visible difference is that it removes the repaint a late stylesheet would cause.

### How it decides

The first `</head>` is the anchor, and every `<style>` block and stylesheet `<link>` after it moves to the end of `<head>`, in original order. A stylesheet inside `<noscript>`, or a `<style scoped>` block, stays where the author put it, and styles after it are not moved either. When `move_css_above_scripts` is also enabled, the first anchor in the document, the `</head>` or the first `<script>`, decides where the styles go.

## When to use it

- Not in CoreFilters: it runs only when you enable it by name.
- Risk rating on these docs: **Generally safe**.

## Risks

- Stylesheets a script deliberately placed in the body, for example one that swaps a `<link>` after load, are moved as well; disable the filter for such pages.

- Verify with the `X-Mod-Pagespeed` response header and a `?PageSpeedFilters=-move_css_to_head` comparison; [Is it working?](https://modpagespeed.com/docs/is-it-working/) has the steps.

## Configuration

Enable it in the module configuration, at server, virtual-host or location scope:

**Apache**

```apache
ModPagespeedEnableFilters move_css_to_head
```

**nginx**

```nginx
pagespeed EnableFilters move_css_to_head;
```

**IIS (pagespeed.config)**

```text
pagespeed EnableFilters move_css_to_head
```

```html
<!-- before -->
<head>
  ...no stylesheet for comments...
</head>
<body>
  <article>...</article>
  <link rel="stylesheet" href="/css/comments.css" />
</body>

<!-- after: the link is appended to the end of head -->
<head>
  ...
  <link rel="stylesheet" href="/css/comments.css" />
</head>
<body>
  <article>...</article>
</body>
```

### On the worker

The worker runs its own pipeline, configured by flags. Its equivalent of this filter is the [async css](https://modpagespeed.com/docs/worker-configuration/#async-css) transform. Toggleable via [`--no-async-css`](https://modpagespeed.com/docs/worker-configuration/#--no-async-css); the coverage gate is tuned with [`--async-css-min-coverage`](https://modpagespeed.com/docs/worker-configuration/#--async-css-min-coverage) and [`--async-css-min-deferred-bytes`](https://modpagespeed.com/docs/worker-configuration/#--async-css-min-deferred-bytes).

Scoping, `ForbidFilters` and the thresholds filters read: [Choosing filters](https://modpagespeed.com/docs/filter-selection/).

## Live example

See the before and after, with the source diff: [move_css_to_head example](https://modpagespeed.com/examples/move_css_to_head/).

## Frequently asked questions

**What does the move_css_to_head filter do?**

`move_css_to_head` collects stylesheet references that sit in the `<body>` and appends them to the end of `<head>`. Stylesheets discovered in the body still apply, but the browser finds them late in the parse, and a late stylesheet can repaint content that was already shown without it. Gathering them into `<head>` puts every download where the browser expects stylesheets and starts them all at once.

**Is move_css_to_head enabled by default?**

No. `move_css_to_head` is not in CoreFilters, the default RewriteLevel; it runs only when you enable it by name with `EnableFilters`.

**How do I enable move_css_to_head on Apache and nginx?**

Add `ModPagespeedEnableFilters move_css_to_head` on Apache or `pagespeed EnableFilters move_css_to_head;` on nginx. On IIS, add `pagespeed EnableFilters move_css_to_head` to pagespeed.config.

## Related filters

- [`combine_css`](https://modpagespeed.com/docs/filters/combine_css/): Combine CSS
- [`compute_critical_css`](https://modpagespeed.com/docs/filters/compute_critical_css/): Compute critical CSS
- [`extend_cache_css`](https://modpagespeed.com/docs/filters/extend_cache_css/): Extend cache for CSS
- [`fallback_rewrite_css_urls`](https://modpagespeed.com/docs/filters/fallback_rewrite_css_urls/): Fallback CSS URL rewriting
- [`flatten_css_imports`](https://modpagespeed.com/docs/filters/flatten_css_imports/): Flatten CSS @imports
- [`inline_css`](https://modpagespeed.com/docs/filters/inline_css/): Inline CSS
- [`inline_google_font_css`](https://modpagespeed.com/docs/filters/inline_google_font_css/): Inline Google Fonts CSS
- [`inline_import_to_link`](https://modpagespeed.com/docs/filters/inline_import_to_link/): Convert @import to link

This page is drawn from the [move_css_to_head entry on CSS filters](https://modpagespeed.com/docs/css-filters/#move_css_to_head). Every filter in one table: [PageSpeed filters](https://modpagespeed.com/docs/filters/).
