I've compiled Nginx 1.4.3 from source with the SPDY module.

However when SPDY is enabled, it seems to break my 'Vary: Accept-Encoding' header.

My Nginx Configuration:

--with-http_ssl_module --prefix=/usr

My `nginx.conf' file:

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #Compression Settings
    gzip on;
    gzip_http_version 1.0;
    gzip_comp_level 2;
    gzip_proxied any;
    gzip_min_length  1100;
    gzip_buffers 16 8k;
    gzip_types text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml;
    # Some version of IE 6 don't handle compression well on some mime-types, 
    # so just disable for them
    gzip_disable "MSIE [1-6].(?!.*SV1)";
    # Set a vary header so downstream proxies don't send cached gzipped 
    # content to IE6
    gzip_vary on;

    proxy_cache_path /var/www/nginx_cache levels=1:2 keys_zone=qnx-cache:10m inactive=24h max_size=1g;
    proxy_temp_path /var/www/nginx_cache/tmp;

    server_tokens off;

    include /etc/nginx/conf.d/*.conf;

    map $geo $mapping {
        default default;
        US US;
        DE DE;
    CA CA;
    GB GB;
    geo $geo {
        default default;
        include geo.conf;
    upstream default.backend {
#   sticky;
    upstream mysite.backend {
        sticky name=servIDTrack hash=sha1;
        server weight=10 max_fails=3 fail_timeout=10s;
        server weight=10 max_fails=3 fail_timeout=10s;
        server weight=10 max_fails=3 fail_timeout=10s;
server {
        listen      80;
        server_name secure.mysite.com;
        return 301 https://$server_name$request_uri;
server {
        listen 443 ssl;
        server_name secure.mysite.com;
        more_set_headers    "Server: X-nginx/v1.1 [LB01]";

        ssl_certificate     /etc/nginx/ssl/secure_mysite_com_ssl.cert;
        ssl_certificate_key /etc/nginx/ssl/secure_mysite_com_ssl.key;

        ssl_protocols           SSLv3 TLSv1 TLSv1.1 TLSv1.2;
#       ssl_ciphers             RC4:HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers on;
        keepalive_timeout       120;
        ssl_session_cache       builtin:1000 shared:SSL:10m;
        ssl_session_timeout     10m;

        location / {
            proxy_pass http://mysite.backend;
            proxy_set_header        Host            $host;
            proxy_set_header        X-Real-IP       $remote_addr;
            proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_redirect          off;
            proxy_http_version      1.1;

            add_header Strict-Transport-Security "max-age=31556926; includeSubdomains";

    # Cache
                proxy_cache qnx-cache;
                proxy_cache_valid  200 301 302  120m;
                proxy_cache_valid 404 1m;
                add_header X-Cache-Status $upstream_cache_status;
                proxy_cache_key "$scheme$host$request_uri";


Header results with SPDY enabled/disabled:

url: mypage.php (SPDY enabled)

HTTP/1.1 200 OK
cache-control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
content-encoding: gzip
content-type: text/html; charset=utf-8
date: Wed, 20 Nov 2013 13:39:30 GMT
expires: Thu, 19 Nov 1981 08:52:00 GMT
pragma: no-cache
server: X-nginx/v1.1 [LB01]
status: 200
strict-transport-security: max-age=31556926; includeSubdomains
version: HTTP/1.1
x-cache-status: MISS

url: mypage.php (SPDY disabled)

HTTP/1.1 200 OK
Date: Wed, 20 Nov 2013 13:45:00 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Accept-Encoding
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Server: X-nginx/v1.1 [LB01]
Strict-Transport-Security: max-age=31556926; includeSubdomains
X-Cache-Status: MISS
Content-Encoding: gzip

url: mystyle.css (SPDY enabled)

HTTP/1.1 200 OK
date: Wed, 20 Nov 2013 12:53:49 GMT
content-encoding: gzip
last-modified: Mon, 18 Nov 2013 22:09:32 GMT
server: X-nginx/v1.1 [LB01]
x-cache-status: HIT
strict-transport-security: max-age=31556926; includeSubdomains
content-type: text/css
status: 304
expires: Wed, 18 Dec 2013 22:42:35 GMT
cache-control: max-age=2592000
version: HTTP/1.1

url: mystyle.css (SPDY disabled)

HTTP/1.1 200 OK
Date: Wed, 20 Nov 2013 13:45:01 GMT
Content-Type: text/css
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Accept-Encoding
Last-Modified: Mon, 18 Nov 2013 22:09:32 GMT
Cache-Control: max-age=2592000
Expires: Wed, 18 Dec 2013 22:10:13 GMT
Server: X-nginx/v1.1 [LB01]
Strict-Transport-Security: max-age=31556926; includeSubdomains
X-Cache-Status: HIT
Content-Encoding: gzip

As you can see, when SPDY is enabled, the Vary: Accept-Encoding headers disappear.

Is this an issue with the way my nginx.conf is configured?

3 Answers 3


SPDY (and HTTP/2.0) require user agents to support compression and that draws the Vary: Accept-Encoding header useless. That’s why nginx drops the header.

  • I see. So it's a deliberate action to drop the header, rather than a 'bug'? Nov 21, 2013 at 23:55
  • Absolutely, if the user agent doesn’t send the appropriate header that indicates that it accepts compressed content (which is Accept-Encoding: gzip, deflate) nginx answers with an HTTP/1.1 or HTTP/1.0 response. Nov 22, 2013 at 0:03

It may or may not be a bug, but since SPDY always uses compression, and there are currently no SPDY forward proxies (because SPDY always uses TLS), the Vary header is useless. Vary is only used by proxies to determine whether to send compressed or uncompressed content

Basically, if a connecting system supports SPDY, it also supports gzip compressed content, so no need to waste bytes on the Vary header. This is what at least some big SPDY-supporting sites are doing. See this page for further details.

Theoretically there could some day be SPDY proxies in corporations that have legacy browsers behind them that do not support HTTP compression, but I really hope not. Even if there were, the proxy could just decompress on the fly with low overhead if a client did not send an "Accept-Encoding: gzip" header.

  • Thanks for the info, and that link. This makes a lot more sense to me now. Nov 21, 2013 at 23:56
  • I'd like to point out that the Vary header is still very useful and important for HTTP caching. It's only Vary: Accept-Encoding use of Vary that is less useful in SPDY. Nov 17, 2014 at 19:50

SPDY and HTTP/2 have their own dedicated header compression mechanisms. The standards imply that the browser has to support data compression in order to use SPDY or HTTP/2 making the addition of the Vary header redundant. Excluding the Vary header in this case also saves a few bytes in each request message.

A more detailed explanation on that here.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .