window.location.origin or a dynamic relative path instead of hardcoding /api. This ensures that requests resolve correctly regardless of whether the app is served from the root domain or a subdirectory like username.github.io/repo-name.
Understanding the Root Cause
When you host a React application on GitHub Pages, the deployment path can vary significantly. If you deploy to a user site (e.g.,https://username.github.io/), the base path is effectively empty. However, if you deploy to a project site (e.g., https://username.github.io/my-project/), the base path becomes /my-project/. Many developers make the mistake of setting the baseURL in their Axios configuration to a static string like http://localhost:3000/api or even just /api.
This static configuration works perfectly during local development on localhost, but it breaks immediately upon deployment. When the browser attempts to fetch data, it looks for the API endpoint relative to the current location. If your code says axios.get('/api/users') and your site is at https://username.github.io/my-project/, the browser will attempt to reach https://username.github.io/my-project/api/users. While this might seem correct, it often fails if the backend server does not expect requests to include the frontend's repository name in the path. Conversely, if you hardcoded http://localhost:3000/api, the browser will try to hit that exact address, which is inaccessible from a production environment.
The core issue is that Axios does not automatically adjust its base URL based on the hosting environment. It relies entirely on the value you provide in the configuration object. Therefore, the solution requires making the base URL dynamic, allowing it to adapt to the current runtime context of the application.
Configuring Axios Dynamically
The most robust way to solve this is by creating a custom Axios instance where the base URL is determined at runtime. Instead of importing a static configuration, you should define the base URL usingwindow.location.origin. This property returns the protocol, hostname, and port of the current page, ensuring that all subsequent requests are made to the correct domain.
You can create a dedicated file for your API calls, such as api.js or axiosInstance.js. Inside this file, initialize the Axios instance with the dynamic origin. Here is how you should structure the code:
import axios from 'axios';
const api = axios.create({
baseURL: window.location.origin,
timeout: 10000,
});
export default api;
By using window.location.origin, you ensure that if your app is hosted at https://username.github.io/my-project/, the base URL becomes https://username.github.io/my-project/. When you make a request like api.get('/api/users'), Axios will combine the base URL with the endpoint, resulting in https://username.github.io/my-project/api/users. This approach eliminates hardcoding issues and makes your code portable across different environments.
If your backend API is hosted on a completely separate domain (e.g., https://api.yourbackend.com), you should still avoid hardcoding the full URL if possible. Instead, use an environment variable. Create a .env file in your React project root and add:
REACT_APP_API_URL=https://api.yourbackend.com
Then, access it in your Axios configuration:
const api = axios.create({
baseURL: process.env.REACT_APP_API_URL || window.location.origin,
});
This fallback mechanism allows you to switch between local development, staging, and production seamlessly. For GitHub Pages specifically, since you cannot control the server headers easily, using window.location.origin is often the safest bet if your API is co-located or if you have configured proxying correctly in your build settings.
Handling Relative Paths and Proxy Issues
Another common pitfall involves how the browser handles relative paths when the base URL is set dynamically. If you do not set abaseURL and rely solely on relative URLs in your components, Axios will append the endpoint to the current window location. For example, if you call axios.get('/api/users') without a baseURL, and your current URL is https://username.github.io/my-project/dashboard, the request will go to https://username.github.io/my-project/dashboard/api/users, which is incorrect.
This is why setting the baseURL is critical. It establishes a consistent anchor point for all relative requests. Additionally, if you are using Create React App or Vite, you might be accustomed to using a proxy in package.json or vite.config.js. Proxies work well during local development by forwarding requests from localhost to the backend. However, proxies defined in these files do not exist in the production build. They are purely a development tool. Therefore, relying on a proxy for production API calls will result in failed requests.
To bridge this gap, you must ensure that your production environment handles CORS (Cross-Origin Resource Sharing) correctly. If your frontend and backend are on different domains, your backend server must include appropriate CORS headers (Access-Control-Allow-Origin). If they are on the same domain (same origin), CORS is not a concern, and the dynamic window.location.origin method works flawlessly.
For more insights on handling pending requests and connection issues that may arise during these transitions, you can refer to our guide on React Axios Call Pending After Multiple Requests. This article details how to manage request lifecycles when network conditions change, which is particularly relevant when switching from local to remote environments.
Verifying the Configuration
Once you have updated your Axios configuration, verify that it is working correctly. Open your browser's developer tools and navigate to the Network tab. Trigger an API call within your React application. Look at the "Request URL" column. It should display the full, correct URL including the protocol, domain, and any necessary subdirectories. If the URL still points tolocalhost or an incorrect path, double-check that you are importing the custom instance (api) rather than the default axios object. A common error is mixing the two:
// Wrong: Using default axios which has no baseURL
import axios from 'axios';
axios.get('/api/users'); // Fails in production
// Correct: Using the configured instance
import api from './api';
api.get('/api/users'); // Works in production
Ensure that every API call in your components uses the exported api instance. This consistency guarantees that all requests respect the dynamic base URL you have configured.
Frequently Asked Questions
Why does my Axios request fail with a 404 error on GitHub Pages?
This usually happens because the base URL is hardcoded to localhost or an absolute path that doesn't match the deployment directory. Ensure you use window.location.origin or environment variables to construct the base URL dynamically so it adapts to the GitHub Pages subdirectory.
Can I use environment variables for the base URL in production?
Yes, you can use environment variables like REACT_APP_API_URL to define the base URL. In production builds, these variables are replaced with their values, allowing you to maintain different configurations for development and production without changing code.
Does setting baseURL affect relative URLs in other libraries?
No, the baseURL configuration is specific to the Axios instance. Other HTTP clients or fetch APIs do not inherit this setting. Each library must be configured independently to handle base paths correctly.
How do I handle CORS errors when moving to GitHub Pages?
CORS errors occur when the frontend and backend are on different origins. To fix this, you must configure your backend server to send Access-Control-Allow-Origin headers. Alternatively, host both frontend and backend on the same domain to avoid CORS restrictions entirely.
What is the difference between baseURL and the endpoint in Axios?
The baseURL is the constant prefix for all requests made by the instance, such as the domain and version number. The endpoint is the specific resource path, like /users, appended to the base URL. Combining them creates the full request URL.