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 site’s HTTP responses—not WordPress alone. Apache sites can use server configuration or an allowed .htaccess file; Nginx sites must use the Nginx server configuration; managed hosts may require a support request or control-panel setting. Set long lifetimes only for suitable static assets, preferably files whose URLs are versioned, and confirm the headers on the live site afterward.

What an Expires header does

An Expires response header gives a browser a date after which a cached response should be considered stale. WordPress documentation groups it with Cache-Control (especially max-age) and ETags as browser-caching controls for repeat requests to static resources such as images, CSS, and JavaScript. See the WordPress caching guidance.

When both headers are present, Cache-Control governs modern browsers: “The older Expires header is still commonly sent for compatibility, but Cache-Control takes precedence when both are present.” Therefore, adding only an Expires date may not produce the behavior you expect if another layer sends a conflicting Cache-Control value.

Identify where your response is configured

Before editing anything, determine which component serves the requested file and can modify its headers.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • Apache: a server configuration file or an allowed per-directory .htaccess file can send headers. WordPress commonly uses .htaccess for rewrite rules, but the host must permit the required overrides and modules.
  • Nginx: Nginx does not read .htaccess. Headers belong in the Nginx server configuration or a hosting control panel that writes it.
  • Managed hosting, CDN, or reverse proxy: the host or CDN may add, replace, or cache headers after WordPress generates a response.
  • Caching plugin: a plugin may change response behavior, but an Apache-only plugin cannot configure Nginx. The WordPress.org listing for Leverage Browser Caching states that it writes Apache rules to .htaccess and has no effect on Nginx or IIS: plugin documentation.

Apache: add headers through server configuration or .htaccess

Use the main Apache configuration when you control the server; use .htaccess only when the host allows the necessary directives. WordPress explains the file’s per-directory role and Apache header directives in its Apache HTTPD/.htaccess documentation.

Prepare safely

  1. Make a recoverable copy of the existing .htaccess file and preserve WordPress’s current rewrite block.
  2. Confirm that Apache has the header and expiration functionality enabled and that your hosting plan permits those directives. A syntax error can produce a server error, so use your host’s validation or rollback procedure.
  3. Choose only the static file extensions you intend to cache. Do not apply a long browser lifetime to logged-in pages, account areas, checkout responses, API output, or other personalized content.

Add a scoped rule

Apache can send an absolute Expires date and a matching Cache-Control: max-age value. The exact syntax and available modules vary by host, so treat the following as a pattern to adapt rather than a universal drop-in:

<IfModule mod_expires.c>
  ExpiresActive On
  ExpiresByType image/jpeg "access plus 1 year"
  ExpiresByType image/png "access plus 1 year"
  ExpiresByType image/gif "access plus 1 year"
  ExpiresByType image/webp "access plus 1 year"
  ExpiresByType text/css "access plus 1 month"
  ExpiresByType application/javascript "access plus 1 month"
</IfModule>

<IfModule mod_headers.c>
  <FilesMatch ".(css|js|jpg|jpeg|png|gif|webp)$">
    Header set Cache-Control "public, max-age=2592000"
  </FilesMatch>
</IfModule>

Adjust file types, durations, and module availability to your deployment. A one-year lifetime is appropriate only where the URL changes when the file changes; otherwise visitors can retain an old file until it expires. Existing host, plugin, or CDN rules may override this block.

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

Prefer versioned assets for long lifetimes

WordPress can version enqueued styles and scripts through the enqueue function’s version argument, which appends a version query string. When that version changes, the browser requests a new URL. The WordPress Hosting Handbook recommends this pattern when using long cache lifetimes.

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.

Nginx: configure the server, not .htaccess

Nginx ignores .htaccess, so placing Apache directives there cannot add headers to Nginx responses. If you administer the server, add an appropriate expires and/or add_header Cache-Control policy inside the relevant server or location configuration, scoped to static assets. Validate the configuration, reload Nginx, and then inspect a real response.

If you do not have Nginx configuration access, ask the managed host to apply the policy or use its documented cache controls. A WordPress plugin that edits .htaccess cannot change Nginx behavior.

Keep dynamic and private WordPress responses out of long caches

Static-file caching and WordPress’s no-cache behavior solve different problems. Do not replace no-cache headers on administrative, personalized, or otherwise private responses with a broad long-lived rule. The wp_get_nocache_headers() reference documents an Expires value in the past and Cache-Control: no-cache, must-revalidate, max-age=0, no-store, private. Since WordPress 6.8.0, that Cache-Control value includes no-store and private regardless of login status. Scope your server rules to the static paths or extensions they are meant to cover.

Verify the headers on the live site

  1. Open your browser’s developer tools, select the Network panel, and request a representative CSS file, JavaScript file, image, and—separately—a dynamic page.
  2. Inspect each response’s headers. Confirm the returned Expires date and Cache-Control value, including max-age, rather than relying on the contents of a configuration file.
  3. Repeat the check in a private window or with another header-inspection method, and check a cache-busting query string if your site uses one.
  4. Record which server, CDN, or cache served the response; an upstream layer can replace headers after Apache, Nginx, or WordPress sets them. Apache’s documentation describes viewing HTTP headers with browser developer tools and other network inspection tools.

When the headers are not working

The file was edited but no header changed

  • Verify that the request is reaching Apache rather than Nginx, a CDN, or another proxy.
  • Check that Apache permits .htaccess overrides and that the needed modules are enabled.
  • Look for later rules in the host, plugin, or CDN configuration that replace Cache-Control or Expires.

Apache returns an error after editing .htaccess

Restore the backup immediately, then have the host validate module availability and directive permissions. Do not leave an untested directive in production.

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

Assets remain stale after an update

The browser is honoring the configured lifetime. Change the asset’s version so WordPress emits a new URL, or use a shorter lifetime for files whose URLs cannot change.

Pages appear cached when they should be private

Inspect the dynamic response for private, no-store, and related no-cache directives. Narrow any broad static-file pattern and review CDN or plugin page-cache rules so personalized responses are excluded.

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

Choosing a practical policy

Resource or situation Suitable approach Reason
Versioned CSS, JavaScript, fonts, or images Long max-age with a matching Expires policy A changed version produces a new URL, limiting stale-file risk.
Unversioned assets that change in place Shorter lifetime and planned cache purges Visitors otherwise may keep the old URL until expiration.
Login, account, checkout, admin, or personalized responses Retain WordPress or application no-cache/private headers These responses may contain user-specific or sensitive data.
Site on Nginx without server access Request a host-level change .htaccess edits cannot affect Nginx.

For broader performance practices, see WordPress’s Optimization guidance.

Frequently Asked Questions

Do I need both Expires and Cache-Control?

They can be sent together for compatibility, but browsers use Cache-Control when both are present. Set and verify the Cache-Control policy rather than relying on Expires alone.

Free tools Windows power users keep installed

One-click scans. No signup required.

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

Can a WordPress plugin add Expires headers on Nginx?

Not through .htaccess. Nginx must receive the policy in its server configuration or from the host/CDN; an Apache-only plugin cannot change Nginx responses.

How long should WordPress browser caching last?

There is no universal duration. Choose it from the asset’s update strategy: long lifetimes are safer for versioned URLs, while frequently changing unversioned files need shorter lifetimes.

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.