Fix CORS Missing Access-Control-Allow-Origin in XMLHttpRequest
Resolve 'Missing CORS header Access-Control-Allow-Origin' error in VM Essentials plugin's status-update.js. Server-side PHP fixes for Joomla, handle preflights, add headers, and test XMLHttpRequest requests effectively.
How to fix ‘Missing CORS header “Access-Control-Allow-Origin”’ error in XMLHttpRequest from VM Essentials plugin’s status-update.js?
The plugin is causing a CORS error. I’ve identified the issue in the status-update.js file. Here is the code:
(function (vme, document) {
vme.updateStatus = function (action) {
var request = new XMLHttpRequest();
request.getResponseHeader('Content-type', 'application/json');
request.onload = function () {
var result = JSON.parse(this.responseText);
if (!result.error) {
if (result.products && result.products.length) {
result.products.forEach(function (productId) {
vme.addedAction(action, productId);
})
}
}
}
request.onerror = function (e) {
console.log(e);
}
request.open('GET', vme.base + 'index.php?option=com_vmessentials&task=' + action + '.getStatus&format=json&_' + Date.now() + '=1', true);
// via Cache-Control header:
request.setRequestHeader('Cache-Control', 'no-cache, no-store, max-age=0');
// fallbacks for IE and older browsers:
request.setRequestHeader('Expires', 'Tue, 01 Jan 1980 1:00:00 GMT');
request.setRequestHeader('Pragma', 'no-cache');
request.send();
}
document.addEventListener('DOMContentLoaded', function (event) {
setTimeout(function () {
vme.updateStatus('compare');
vme.updateStatus('wishlist');
}, 100);
});
})(vme, document);
What changes are needed to resolve the CORS issue? Should this be fixed client-side or server-side (e.g., in the PHP endpoint)?
The “Missing CORS header ‘Access-Control-Allow-Origin’” error hitting your VM Essentials plugin’s status-update.js stems from the Joomla PHP endpoint not responding with proper CORS headers for XMLHttpRequest calls. You can’t reliably fix this client-side—browsers enforce the policy strictly—but server-side changes to add Access-Control-Allow-Origin (and handle preflights) will resolve it completely. Update the com_vmessentials component’s PHP task handler with targeted headers, or tweak Joomla’s global settings for a quick win.
Contents
- Understanding the CORS Error
- Client-Side Code Issues in status-update.js
- Why Server-Side is the Real Fix
- Adding CORS Headers in PHP
- Joomla-Specific Solutions
- Testing and Debugging
- Sources
- Conclusion
Understanding the CORS Error
Picture this: your JavaScript in status-update.js fires off an XMLHttpRequest to index.php?option=com_vmessentials&task=compare.getStatus&format=json. The server responds fine—Postman or curl would grab it no problem—but the browser slams the door with “No ‘Access-Control-Allow-Origin’ header is present.” Why? Browsers block cross-origin requests by default to protect users from malicious sites reading sensitive data.
This CORS policy demands the server explicitly say, “Yeah, this origin is cool,” via the Access-Control-Allow-Origin header. Your VM Essentials plugin lives in Joomla, so the request likely hits the same domain (same-origin), but subtle mismatches—like ports, subdomains, or protocol shifts—can trigger it. Or maybe it’s a preflight OPTIONS request your endpoint ignores. Either way, MDN nails it: without that header, XMLHttpRequest’s onload never fires, and onerror catches the fallout.
Common culprits here? Joomla’s routing, caching, or security plugins stripping headers. And that getResponseHeader line in your code? It’s misplaced and ignored, but it hints at deeper confusion.
Client-Side Code Issues in status-update.js
Let’s eyeball your JS first. It’s mostly solid—async GET with cache-busting timestamps—but a couple glitches won’t help CORS and could mask issues.
That line request.getResponseHeader('Content-type', 'application/json');? Total red herring. getResponseHeader reads response headers after the request completes, not sets them. Call it in onload, like this.getResponseHeader('Content-Type'). For a GET, you don’t need a request Content-Type anyway. Ditch it.
Your cache headers look good—no-cache prevents stale data—but add some error logging:
request.onerror = function (e) {
console.error('XHR failed:', e, 'Status:', request.status, 'Response:', request.responseText);
};
In onload, check request.status === 200 before parsing JSON. But here’s the kicker: no client-side hack bypasses CORS. Proxies? JSONP? Not for your JSON endpoint. Browser extensions disable it temporarily, but that’s dev-only. Real fix lives on the server.
Why Server-Side is the Real Fix
Client-side tweaks polish the edges, but CORS is a server-to-browser promise. Your PHP task (like compare.getStatus) must echo back headers permitting the origin. Wildcard * works for public APIs, but lock it to your domain for security—say, https://yourjoomla.com.
StackHawk breaks it down: for simple GETs like yours, just add the origin header. But XMLHttpRequest with custom headers (your Cache-Control) triggers preflight OPTIONS. Server must respond to OPTIONS with Access-Control-Allow-Methods: GET, Access-Control-Allow-Headers: Cache-Control, etc., and 204 status.
Postman skips CORS checks—it’s not a browser—so it works there. Stack Overflow echoes this: browsers enforce it, servers enable it.
Short version? Edit the PHP controller in com_vmessentials, or Joomla’s .htaccess/global config. Server-side wins every time.
Adding CORS Headers in PHP
Dive into your plugin’s PHP—likely components/com_vmessentials/controllers/compare.php or similar. Hook into the task dispatcher early.
Grab this battle-tested snippet from enable-cors.org, tweaked for Joomla:
<?php
// At the top of your controller or a helper function
function handleCORS($allowedOrigins = ['https://yourdomain.com']) {
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
if (in_array($origin, $allowedOrigins) || in_array('*', $allowedOrigins)) {
header("Access-Control-Allow-Origin: " . $origin);
header("Access-Control-Allow-Credentials: true");
header("Vary: Origin");
}
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, Cache-Control, Authorization, X-Requested-With");
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
header("Access-Control-Max-Age: 86400");
http_response_code(204);
exit;
}
}
// Call it before any output
handleCORS();
Stick handleCORS(); right after Joomla’s defined('_JEXEC') or die; in the task method. For dynamic origins, validate $_SERVER['HTTP_ORIGIN'] against your site.
A simpler gist version for quick tests: this PHP CORS handler. Drop it in, swap * for your domain. Boom—headers flow.
Joomla might buffer output, so ob_start() early if headers vanish.
Joomla-Specific Solutions
VM Essentials is Joomla turf, so plugin tweaks might not persist updates. Broader fixes:
-
Global Config: Joomla 4+ has Servers > CORS Headers. Set “Enable CORS” to Yes, specify origins. Forum users swear by it.
-
.htaccess: Apache? Add to
.htaccess:
<IfModule mod_headers.c>
Header always set Access-Control-Allow-Origin "https://yourdomain.com"
Header always set Access-Control-Allow-Methods "GET, POST, OPTIONS"
Header always set Access-Control-Allow-Headers "Cache-Control, Content-Type"
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=204,L]
</IfModule>
- HttpHeaders Plugin: Install this extension. It adds custom headers site-wide without code hacks. Forums note it overrides server defaults nicely: Joomla thread.
Pick per-site if multi-domain; global for single-site simplicity.
Testing and Debugging
Fixed? Verify in Chrome DevTools > Network. Reload, check the request:
- OPTIONS preflight? 204 with CORS headers.
- GET? 200 with
Access-Control-Allow-Origin: yourdomain.com.
Console clear? Great. Firefox/Edge too—cross-browser sanity.
Stuck? curl -H "Origin: https://yourdomain.com" -X OPTIONS your-endpoint simulates preflight. Or browser flags: chrome://flags/#block-insecure-private-network-requests (disable if localhost woes).
Edge case: Joomla caching (JCH Optimize?). Purge it. HTTPS/HTTP mix? Force HTTPS.
Your updated JS with better logging spots client gremlins fast.
Sources
- Reason: CORS header ‘Access-Control-Allow-Origin’ missing - HTTP | MDN
- How to Fix “No ‘Access-Control-Allow-Origin’ Header Present” | StackHawk
- No ‘Access-Control-Allow-Origin’ header error while Postman works | Stack Overflow
- Enable CORS in PHP | enable-cors.org
- Handle CORS in PHP Gist
- CORS errors on Joomla sites | Joomla Forum
- Blocked by CORS policy in Joomla | Joomla Forum
Conclusion
Server-side CORS headers in your VM Essentials PHP endpoint—or Joomla’s global tools—zap this XMLHttpRequest error for good, letting status updates flow smoothly. Start with a targeted PHP function for precision, fall back to .htaccess or plugins for ease. Test rigorously across browsers, and you’ll dodge future headaches. Quick win keeps your wishlist/compare humming without browser drama.