If you haven't read yesterday's tip on Vite and remote sites loading modules via script tag, start there first for full context.
So what is the solution? It doesn't actually involve Laravel.
Normally, we could configure our CORS headers in the config/cors.php
file, but that only works if Laravel is serving the request. In our case, the built assets don't go through Laravel. Our web server serves them directly (and more efficiently).
We could force Laravel to serve up our built assets just so we could have it add the CORS headers for us, but that feels like a hacky solution and has performance implications.
Instead, we can use our web server to specify the CORS headers. And we can be selective so that we don't add unnecessary headers to every request.
Here's how to do it with Nginx:
# inside our nginx.conf, before our index.php and php-fpm directives
location ~ ^/build/js/embed/(.*)$ {
if ($request_method = 'OPTIONS') {
# we're using a wildcard origin here, but you could specify exact domains if you need
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
if ($request_method = 'POST') {
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
}
if ($request_method = 'GET') {
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
}
}
I know what you're thinking: couldn't you merge some of those if
blocks? Nope, nginx handles if
differently than PHP and this is actually the cleanest way to do it with those constraints.
Similar options exist for other web servers. enable-cors.org is a great reference (and where I got most of the above config from).
Or maybe you're publishing your assets to a CDN that sets these headers for you already. If so, you're all set.
Here to help,
Joel
P.S. After reading this tip, if your brain is in a security frame of mind, check out our free book on Laravel security.