Search code examples
google-chromenginxservercorsmedia

Why is CORS working for API requests but not media files (e.g. mp4, webm) on my Nginx config?


I have set up an Nginx server on Ubuntu that serves up two website. One is a Wordpress delivering its API content, the other is a frontend app consuming the API using Axios.

I can query and read the JSON content from the API, and inspecting the response headers matches that of my CORS config. But on Chrome, I'm getting a CORS error in console on all media files (.mp4, .webm) the application is trying to consume after reading their URI from the API content.

Access to video at 'https://api.example.com/wp-content/uploads/2019/05/Seiho-Edited_compress.mp4' 
from origin 'https://next.example.com' 
has been blocked by CORS policy: 
No 'Access-Control-Allow-Origin' header is present on the requested resource.

It works properly on Firefox.

Note that pasting the media URI in the address bar in Chrome displays the media as expected. Also not that the video tags use crossorigin="anonymous".

What am I doing wrong?

Below is my Nginx config, in case there's some glaring issue I missed.

server {
    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

    root /var/www/example.com/backend;
    server_name api.example.com;
    access_log /var/log/nginx/unicorn_access.log;
    error_log /var/log/nginx/unicorn_error.log;

    charset                     utf-8;
    gzip                        off;

    # Set CORS policy
    set $cors_origin            "";
    set $cors_cred              "";
    set $cors_header            "";
    set $cors_method            "";

    if ($http_origin ~ '^https?:\/\/(localhost|next.example\.com)$') {
        set $cors_origin        $http_origin;
        set $cors_cred          true;
        set $cors_header        $http_access_control_request_headers;
        set $cors_method        $http_access_control_request_method;
    }

    add_header Access-Control-Allow-Origin      $cors_origin;
    add_header Access-Control-Allow-Credentials $cors_cred;
    add_header Access-Control-Allow-Headers     $cors_header;
    add_header Access-Control-Allow-Methods     $cors_method;

    location / {
        index                   index.php index.html;
        try_files               $uri $uri/ /index.php?$args;
    }

    client_max_body_size        50m;

    # Add trailing slash to */wp-admin requests.
    rewrite /wp-admin$ $scheme://$host$uri/ permanent;

    # Prevents hidden files (beginning with a period) from being served
    location ~ /\. {
        access_log              off;
        log_not_found           off;
        deny                    all;
    }

    # Send 'expires' headers and turn off 404 logging
    location ~* ^.+.(xml|ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|css|rss|atom|js|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
        access_log              off;
        log_not_found           off;
        expires                 max;
    }

    # Pass all .php files onto a php-fpm or php-cgi server
    location ~ \.php$ {
        try_files               $uri =404;
        include                 /etc/nginx/fastcgi_params;
        fastcgi_read_timeout    3600s;
        fastcgi_buffer_size     128k;
        fastcgi_buffers         4 128k;
        fastcgi_param           SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass            unix:/run/php/php7.2-fpm.sock;
        fastcgi_index           index.php;
    }

    # Robots
    location = /robots.txt {
        allow all;
        log_not_found off;
        access_log off;
    }

    # Restrictions
    location ~* /(?:uploads|files)/.*\.php$ {
        deny all;
    }
}


Solution

  • It's pretty common for error responses to not have the appropriate CORS/Access-Control-* headers.

    Next time you run into this, open your browser's developer tools and inspect the HTTP response. It's likely you'll see some error status instead of a 2xx.

    You mentioned that clearing cookies resolved the issue. I'm guessing that there was some error upstream in your application, and that clearing the cookies simply worked around that error condition. Therefore, CORS responses started working again, as expected.