October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run ScanOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
VGSources
Blog

How to Add Expires Headers in WordPress (Apache, Nginx, and Managed Hosting)

A practical guide to adding Expires headers in WordPress without caching private pages: identify your server, configure Apache or Nginx correctly, version static assets, and verify the live response headers.
WorldWordPress Length10 min Posted Quest giverVGSources Team
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

To add Expires headers in WordPress, configure the web server or caching layer that sends your files—not WordPress itself. Use an Apache server rule (usually in .htaccess), an Nginx server-block rule, or your host/CDN control panel. Apply long lifetimes to cache-safe static files such as versioned CSS, JavaScript, fonts, and images; keep personalized, administrative, and other dynamic responses on WordPress’s no-cache policy.

After saving a change, inspect the headers returned by the live site. A rule sitting in a file is not proof that the request used that rule: a different server, CDN, cache plugin, virtual host, or disabled module may be generating the response.

As an Amazon Associate I earn from qualifying purchases.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What an Expires header does in WordPress

An Expires header tells a browser the date and time after which a response should be considered stale. While it is still widely sent for compatibility, modern caching decisions normally use Cache-Control. The WordPress Hosting Handbook states: “The older Expires header is still commonly sent for compatibility, but Cache-Control takes precedence when both are present.” See the WordPress Hosting Handbook performance guidance.

WordPress’s own performance documentation describes browser caching as a way to avoid repeatedly downloading static resources such as images, stylesheets, and JavaScript. The server sends the response headers, and the browser decides whether a stored copy can be reused. WordPress can influence asset URLs and dynamic responses, but there is no single WordPress setting that adds the headers on every installation. The relevant configuration may live in Apache, Nginx, a managed host, a reverse proxy, a CDN, or a caching plugin. The WordPress cache handbook explains the roles of Cache-Control, Expires, and ETags.

Expires versus Cache-Control

  • Expires: an absolute HTTP date, useful for older clients and compatibility.
  • Cache-Control: directives such as max-age, public, private, and no-store. When both headers exist, these directives govern the cache decision.
  • ETag and Last-Modified: validators that let a browser revalidate a stored response instead of downloading the complete body again.

For a practical WordPress setup, send both an appropriate Cache-Control policy and an Expires value for compatible static assets. Do not use a long public lifetime simply because a file has a familiar extension; first decide whether an old copy is acceptable.

Identify the configuration layer before editing anything

Use this order of checks so you do not edit the wrong file or create conflicting rules.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  1. Identify the HTTP server. Apache can read .htaccess when the host permits overrides. Nginx does not read .htaccess; its rules belong in the server configuration. IIS has a different configuration model.
  2. Check who controls that configuration. On a self-managed VPS or dedicated server, you may edit the virtual-host/server-block files. On managed WordPress hosting, the provider may require a support request or expose a control-panel switch instead.
  3. List other response-changing layers. A CDN, reverse proxy, page-cache system, or WordPress caching plugin can add, replace, or remove headers after the origin server has generated them.
  4. Choose representative files. Select one stylesheet, one JavaScript file, one image, and—if relevant—one font from the real site. Also select a dynamic page or logged-in/admin URL that must not receive a public, long-lived policy.

The WordPress Apache handbook describes .htaccess as a distributed, per-directory configuration file that WordPress commonly uses for pretty permalinks. It also notes that the exact directives depend on enabled modules, permitted overrides, file types, and existing host or plugin rules; there is no universal snippet that can be pasted safely into every site. Read the Apache HTTPD and .htaccess documentation before changing production configuration.

Apache: add headers with .htaccess or the virtual-host configuration

Apache is the usual path when your host gives you access to the document root and permits header or expiration directives. If you administer the server directly, a virtual-host configuration is generally preferable because it is centralized and avoids per-directory parsing. If you use shared hosting, .htaccess may be the only available route.

Rank #2
Sale
Web Design All-in-One for Dummies
  • Used Book in Good Condition

Make a recoverable change

  1. Back up the current .htaccess file and record its permissions.
  2. Open the file in the WordPress document root—the same directory that contains wp-admin, wp-content, and wp-includes.
  3. Preserve the existing WordPress rewrite block. Do not replace it with a caching snippet.
  4. Confirm with the host that the required Apache modules are enabled and that the relevant directives are allowed in .htaccess. A server can return a 500 error when an unsupported directive is used.

Scope the rule to static file types

A typical Apache implementation enables expiration handling and assigns a long lifetime to files that are normally static. The exact syntax and supported modules vary, so treat the following as a pattern to adapt to your server’s documentation rather than a universal WordPress setting:

ExpiresActive On
ExpiresByType text/css "access plus 1 year"
ExpiresByType text/javascript "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
ExpiresByType image/avif "access plus 1 year"
ExpiresByType image/webp "access plus 1 year"
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/svg+xml "access plus 1 year"
ExpiresByType font/woff2 "access plus 1 year"

Use a one-year value only when the URL changes whenever the file changes. If your theme or plugin overwrites an unversioned file at the same URL, visitors can keep the old copy until the lifetime ends. For assets that are not versioned, choose a shorter lifetime or add a reliable invalidation process.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

When to use Header directives

Apache’s header module can set or merge explicit Cache-Control and Expires fields, but the directive must be supported and must not fight an existing cache policy. Ask the host which module is active and whether a CDN or page-cache layer rewrites origin headers. Combining several snippets that each set different values often produces confusing results; keep one documented policy per asset class.

Recover from an Apache error

  • If the site immediately returns HTTP 500, restore the backup through the host file manager, SFTP, or console, then test one directive at a time.
  • If only some directories fail, check whether an inner .htaccess file overrides the parent policy.
  • If the file saves but headers do not change, the request may be served by Nginx, a proxy, or a cache that does not read the file.

Nginx: configure the server block, not .htaccess

Nginx never reads Apache’s .htaccess files. Adding Apache directives there cannot enable browser caching on an Nginx site. The WordPress.org listing for the Leverage Browser Caching plugin explicitly says its .htaccess-based approach works exclusively on Apache and has no effect on Nginx or IIS.

Use the Nginx configuration you control

  1. Find the active server block for the site, or ask the hosting provider where response headers are configured.
  2. Place a narrowly scoped location rule for static extensions. Keep the rule outside WordPress’s PHP or administrative locations.
  3. Set a matching Cache-Control policy and an expiration value for those files. The exact syntax and reload command depend on your distribution and hosting stack.
  4. Validate the configuration with the host’s supported test command before reloading Nginx. Reload only after the test succeeds.

A representative Nginx pattern looks like this, but it must be adapted to the provider’s include structure and existing rules:

location ~* \.(css|js|avif|webp|jpg|jpeg|png|svg|woff2)$ {
    expires 1y;
    add_header Cache-Control "public, max-age=31536000, immutable";
}

immutable is appropriate only when the URL is content-versioned and will never be reused for different bytes. Do not apply this location pattern to HTML, login endpoints, REST responses, checkout or account pages, or any route containing user-specific data.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

If you cannot edit Nginx

Managed hosting commonly hides the server block. Open a support request that specifies the file types, desired Cache-Control and Expires behavior, and the need to leave dynamic/private responses untouched. If a control panel provides browser-cache settings, confirm which server and cache layer it changes and whether a CDN has a separate policy.

Choose a lifetime that matches your update strategy

Longer browser reuse reduces repeat transfers, but it also increases the time an unversioned change can remain stale. The safest candidates for long lifetimes are static assets whose URLs change when their contents change. WordPress supports versioning enqueued styles and scripts through the version argument; changing that value appends a different query string, causing browsers to request a new URL. The Hosting Handbook identifies versioned static files as the safer place for long cache lifetimes.

Response type Typical policy decision Reason
Versioned CSS or JavaScript Long public lifetime; an immutable directive can be considered A changed file receives a new URL
Images and fonts with stable, content-specific URLs Long public lifetime when replacement uses a new URL Repeat visits can reuse the stored bytes
Unversioned theme or plugin assets Shorter lifetime or a dependable purge process The same URL may point to new content
HTML pages with personalization Do not apply a broad public, long-lived rule Old or private content could be served to the wrong visitor
Login, administration, and account responses Retain no-cache/private behavior These responses can contain credentials, sessions, or user data

Version WordPress enqueued assets

When registering or enqueuing a stylesheet or script, provide a version that changes with the file. A theme or plugin can use its own release version, a build number, or another deterministic value. The important property is that a changed file produces a changed URL; a static version that never changes defeats long-lived caching.

Do not append a changing timestamp on every request merely to avoid staleness: that prevents browsers from reusing the file at all. Change the version when the asset changes, then let the long cache policy do its job.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Keep WordPress’s dynamic no-cache behavior separate

Static-file caching and dynamic-response caching are different policies. WordPress’s wp_get_nocache_headers() reference documents an expiration date in the past and a policy containing no-cache, must-revalidate, max-age=0, no-store, and private. Since WordPress 6.8.0, the reference records that no-store and private are included regardless of login status.

Do not overwrite those headers globally with a rule intended for images or scripts. A broad rule that marks every response public for a year can expose stale or personalized content. Scope server directives by extension or a carefully defined static directory, and leave PHP-generated pages and sensitive endpoints to their existing application and host policies.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Managed hosts, CDNs, and caching plugins

A UI-based solution can be appropriate when you do not have server access, but first determine what it actually changes. Some controls alter origin headers; others configure only a CDN edge cache or a page-cache plugin. Those layers can have separate TTLs and can overwrite one another.

Questions to ask a host or provider

  • Is the site served by Apache, Nginx, IIS, or a proxy in front of another server?
  • Can customers set Expires and Cache-Control for static extensions?
  • Does the platform automatically version WordPress assets or purge cached files after deployments?
  • Which URLs are excluded from public caching for logged-in users, carts, accounts, previews, and administration?
  • Is there a separate CDN rule that overrides the origin response?

A caching plugin can be convenient on Apache when it writes valid rules to a writable .htaccess. It cannot make an Nginx server read that file. Verify the plugin’s server compatibility and inspect the final response rather than assuming that an enabled checkbox changed the live headers. The WordPress optimization guidance also emphasizes that caching must fit the server and application setup.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Verify the headers on the live site

Verification is part of the change. Use browser developer tools or another HTTP network-inspection method; Apache’s documentation specifically identifies these tools as a way to view response headers.

  1. Open the site in a private or clean browser window.
  2. Open Developer Tools, select the Network panel, and reload the page.
  3. Filter for a CSS file, JavaScript file, image, and font. Open each request’s response headers.
  4. Record the returned Cache-Control, Expires, ETag, and Last-Modified values.
  5. Check a dynamic page, login-related request, or account page separately. Confirm it has the expected private/no-store or other application policy rather than the static-asset lifetime.
  6. Repeat the check after purging the relevant host, CDN, and plugin caches. A cached old response can conceal a correct origin change.

Inspect the actual asset URL that visitors receive. Looking only at the configuration file, a staging server, or an origin address that the public site does not use cannot establish the production result.

Troubleshoot headers that are missing or wrong

Symptom Likely cause What to check
No Expires or Cache-Control change The request bypasses the edited layer Confirm the active server, virtual host, CDN, and cache plugin; purge caches and inspect the public response
Apache returns 500 after editing Unsupported directive or disallowed override Restore the backup, check enabled modules and AllowOverride settings, then add one supported directive at a time
Nginx ignores .htaccess Expected behavior Move the rule to the Nginx server block or ask the host to apply it
Old CSS or JavaScript remains after deployment URL was not versioned or an intermediate cache was not purged Change the asset version, confirm the generated URL changed, and purge the appropriate cache
Logged-in or personalized content appears cached A broad public rule matched dynamic responses Remove the global rule, restrict it to static extensions, and restore WordPress’s private/no-store behavior
Different headers appear at different times Multiple cache layers or conditional responses Compare headers from a clean session and identify whether the response came from origin, CDN, or page cache

Operational checklist

  • Identify Apache, Nginx, IIS, or a managed proxy before editing.
  • Back up the current configuration and preserve WordPress rewrite rules.
  • Limit long lifetimes to static resources whose URLs change when their contents change.
  • Set Cache-Control deliberately; treat Expires as compatibility support, not the primary policy.
  • Keep administrative, personalized, and private responses out of public long-lived caching.
  • Coordinate origin, CDN, and plugin settings so one layer does not silently override another.
  • Inspect real production responses and test both static and dynamic URLs after every change.

Bottom line

Adding Expires headers in WordPress is a web-server configuration task. Use .htaccess only on Apache installations that permit it, configure Nginx in its server block, or ask a managed host to apply the policy. Pair compatible Expires values with deliberate Cache-Control directives, use long lifetimes only for versioned or otherwise safely replaceable static assets, preserve WordPress’s no-cache behavior for dynamic data, and verify the headers that the live site actually returns.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More quests from Patch Notes

  1. How to Set Up a RedM (RDR2) Server in 2026: License Key, txAdmin and server.cfg, Step by StepBlog11min
  2. Best RedM (RDR2) Server Hosting in 2026: Comparing Four Hosts on Slots, Memory and PriceBlog10min
  3. How to Host a Mindustry Server in 2026: server-release.jar, Port 6567 and the Commands That MatterBlog7min
Recommended PC Tool
Recommended PC Tool
Crashes, No Sound, or Screen Glitches?Free driver scan
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.