I'm struggling with a persistent CORS error when trying to call the Google Distance Matrix API from my client-side JavaScript using fetch on my website. I'm hoping someone can spot what I might be missing, as I've tried several configurations. The Problem:
My website (https://westtechservices.com) makes a fetch request to the Distance Matrix API. The browser console consistently shows the following error:
Access to fetch at 'https://maps.googleapis.com/maps/api/distancematrix/json?origins=[ORIGIN_ADDRESS]&destinations=[DESTINATION_ADDRESS]&units=metric&key=[MY_API_KEY]' from origin 'https://westtechservices.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
This is followed by net::ERR_FAILED and TypeError: Failed to fetch in my script.
My API Key Configuration (Google Cloud Console): API Key: AIzaSyCJFYOqMZny8YWoygLvlHdRBUF6JctFXRo Project ID: travel-calculation-cost Enabled APIs: "Distance Matrix API" is enabled for this project. Billing: Billing is active for this project.
API Key Restrictions: Application restrictions: Currently set to "None". API restrictions: Set to "Restrict key", with only the "Distance Matrix API" selected. Troubleshooting Steps Taken:
Direct URL Test: When I paste the fully formed Distance Matrix API URL directly into my browser (e.g., https://maps.googleapis.com/maps/api/distancematrix/json?origins=14%20Tainui%20Street%2C%20Greymouth%207805%2C%20New%20Zealand&destinations=6%20Ashmore%20Avenue%2CGreymouth%2C%20New%20Zealand&units=metric&key=AIzaSyCJFYOqMZny8YWoygLvlHdRBUF6JctFXRo), I receive a successful JSON response with "status": "OK".
This successful direct URL test only occurs when "Application restrictions" on the API key are set to "None".
If I change "Application restrictions" to "Websites" (and list https://westtechservices.com), the direct URL test then fails with the error: "API keys with referer restrictions cannot be used with this API." Client-Side Fetch Still Fails with "Application restrictions: None": Even with "Application restrictions: None" (the setting that allows the direct URL test to work), the fetch call from my live website https://westtechservices.com still results in the CORS error detailed above.
Standard Troubleshooting:
Waited extensively (hours/days) for API key changes to propagate. Performed hard refreshes and cleared browser cache (for all time). Tested in multiple browsers (Chrome, Firefox) and in their incognito/private modes. Ensured no VPNs or local proxies are active when testing the live site.
Relevant JavaScript Code Snippet (fetchDistanceWithGoogleMaps function): function fetchDistanceWithGoogleMaps(destinationAddress, callback) { const GOOGLE_MAPS_API_KEY = 'AIzaSyCJFYOqMZny8YWoygLvlHdRBUF6JctFXRo'; // My actual key const GREYMOUTH_TOWN_SQUARE_ADDRESS = '14 Tainui Street, Greymouth 7805, New Zealand'; const travelCostStatusElement = document.getElementById('travelCostStatus'); // Assuming this DOM element exists for messages
// Basic check for placeholder key (though my actual key is hardcoded above in this example) if (GOOGLE_MAPS_API_KEY === 'YOUR_GOOGLE_MAPS_API_KEY_HERE' || !GOOGLE_MAPS_API_KEY) { console.warn('Google Maps API key is not set or is invalid.'); if (travelCostStatusElement) travelCostStatusElement.textContent = 'API key not configured.'; return callback('API key not set or invalid.', null); }
const origin = encodeURIComponent(GREYMOUTH_TOWN_SQUARE_ADDRESS); const destination = encodeURIComponent(destinationAddress); const apiUrl = https://maps.googleapis.com/maps/api/distancematrix/json?origins=${origin}&destinations=${destination}&units=metric&key=${GOOGLE_MAPS_API_KEY};
if (travelCostStatusElement) travelCostStatusElement.textContent = 'Calculating travel cost...';
fetch(apiUrl) .then(response => { if (!response.ok) { // Attempt to parse error from Google's JSON response if possible return response.json().then(errData => { throw new Error(HTTP error! status: ${response.status}. Google Maps API: ${errData.error_message || 'Unknown API error from response.json()'}); }).catch(() => { // Fallback if response.json() fails or no error_message throw new Error(HTTP error! status: ${response.status}. Could not parse error response.); }); } return response.json(); }) .then(data => { if (data.status === 'OK' && data.rows[0] && data.rows[0].elements[0] && data.rows[0].elements[0].status === 'OK') { const distanceInMeters = data.rows[0].elements[0].distance.value; const distanceInKm = distanceInMeters / 1000; if (travelCostStatusElement) travelCostStatusElement.textContent = Distance: ${distanceInKm.toFixed(1)} km.; callback(null, distanceInKm); } else { console.error('Google Maps API Error (after response.ok):', data.status, data.rows[0]?.elements[0]?.status, data.error_message); let userMessage = 'Could not calculate distance (API status not OK).'; if (data.rows[0]?.elements[0]?.status === 'NOT_FOUND') { userMessage = 'Address not found by API. Please check and try again.'; } else if (data.rows[0]?.elements[0]?.status === 'ZERO_RESULTS') { userMessage = 'No route found to this address by API.'; } else if (data.status !== 'OK') { userMessage = API Error: ${data.error_message || data.status}. Please contact support.; } if (travelCostStatusElement) travelCostStatusElement.textContent = userMessage; callback(userMessage, null); } }) .catch(error => { console.error('Error during fetch operation:', error); if (travelCostStatusElement) travelCostStatusElement.textContent = 'Network error or failed to fetch. Please check console.'; callback('Error fetching distance. Check console.', null); }); }
My Question for the Community:
Why would the direct URL test to the Distance Matrix API work without issue when "Application restrictions" are "None", but a client-side fetch call from my specified origin (https://westtechservices.com) still be blocked by a CORS policy error (specifically, "No 'Access-Control-Allow-Origin' header is present")?
Is there a subtle difference in how Google handles fetch requests versus direct browser URL requests in this scenario, even when referer restrictions are supposedly disabled? Am I missing a specific request header or fetch option that might be necessary? Any insights or suggestions on how to resolve this CORS block would be incredibly helpful!
Thanks, Ngakora