Frontend Can't Find Backend Through Apache2

Hello,

I am a rookie when it comes to server configuration but I am trying to build my first server that can host my website. I thought that it would be a good idea to practice on a virtual machine before building out the real thing and buying the domain name. My virtual machine is using Ubuntu server 22.04.

My website is a personal website with a blog that I created with react app. It also has a backend that was put together with node.js, express and mysql. The mysql database stores users, passwords, posts and photos which the frontend pulls from.

On my ubuntu server I installed apache2 and mysql-server along with yarn, nodejs and anything else that was required on my way to building it. The frontend’s landing page loads fine when I type the server’s ip address (which I will replace with <server_ipaddress> in the code) into the browser but when I try to log in which requires a password, the frontend can’t find the URL stored on the backend. I have been trying to figure it out for a week but nothing seems to work.

Here is my VirtualHost code:

<VirtualHost *:80>
        ServerName <server_ipaddress>
        DocumentRoot /home/matt/website/client/mjna
       <Directory /home/matt/website/client/mjna>
           Options Indexes FollowSymLinks
           AllowOverride None
           Require all granted
       </Directory>
</VirtualHost>

<VirtualHost *:80>
        ServerName <server_ipaddress>
        DocumentRoot /home/matt/website/api
        <Directory /home/matt/website/api>
        Options Indexes FollowSymLinks
        AllowOverride None
        Require all granted
        </Directory>
        ProxyPreserveHost On
        ProxyPass / http://<server_ipaddress>:8800/
        ProxyPassReverse / http://<server_ipaddress>:8800/
</VirtualHost>

My frontend’s code related to the login page is this:

const authLogin = async(inputs) =>{
        const res = await axios.post("/api/auth/login", inputs);
        setCurrentUser(res.data);

And my backend’s index.js file has this:

const app = express()
app.use("/api/auth", authRoutes)
app.listen(8800, ()=>{
    console.log("Connected")
})

As you can see I am using middleware with the auth.js file in my routes folder looking like this:

const router = express.Router();
router.post("/login", login)
export default router

And my auth.js file in my controller folder looking like this:

export const login = (req, res) => {
    
    const q = "SELECT * FROM user WHERE password = ?"
    
    db.query(q, [req.body.password], (err, data)=>{
        if (err) return res.json(err);
 
        if(data.length === 0) return res.status(404).json("Wrong Password");
        
        const token = jwt.sign({id:data[0].id}, "jwtkey");
        
        res.cookie("access_token", token, {
            httpOnly:true,
        }).status(200).json(data[0].uid);
    });
}

When I enter the password on the website I get this error in the browser’s console:

POST http://<server_ipaddress>/api/auth/login 404 (Not Found)

And on the website this error pops up:

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <html><head> <title>404 Not Found</title> </head><body> <h1>Not Found</h1> <p>The requested URL was not found on this server.</p> <hr> <address>Apache/2.4.52 (Ubuntu) Server at <server_ipaddress> Port 80</address> </body></html>

Apache’s error log doesn’t produce any errors but the apache other_vhosts_access.log produces this error:

"POST /api/auth/login HTTP/1.1" 404 491 "<server_ipaddress>/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36"

Now I’ve checked the backend server typing in <server_ipaddress>:8800 into a browser and I get my dummy html to show that it’s working. There are no firewall issues as I have disabled it. As I understand it, maybe I’m wrong, but it seems like the frontend can’t find the router 8800. Perhaps I have something wrong with my virtualhost code but I have troubleshooted many different possibilities and nothing seems to fix this 404 error. I’m sure the error is simple as I am relatively new with server configuration but I would greatly appreciate if someone could tell me what I have wrong as I feel so close to the finish line of completing this project which has been long in the making.

Thanks,
Matt

I think the problem is that you’re trying to host two different websites on one IP. You can’t do that, as apache can’t just guess which website you want for any given request. Therefore, it’s probably completely ignoring the second configuration block (and it’s probably also warning about this when you start apache).

What you should do instead is create local hostnames and bind apache to that.

The first step is to come up with a hostname for the frontend and the backend. I’m going to assume frontend.dev.myapp.com and backend.dev.myapp.com

Then you need to add these to your /etc/hosts file (you need to be root to edit this file, e.g. using sudo nano /etc/hosts or any other editor you like)

127.0.0.1 frontend.dev.myapp.com backend.dev.myapp.com

Just add it to the bottom of the file

Then change the configuration of Apache to

<VirtualHost *:80>
        ServerName frontend.dev.myapp.com
        DocumentRoot /home/matt/website/client/mjna
       <Directory /home/matt/website/client/mjna>
           Options Indexes FollowSymLinks
           AllowOverride None
           Require all granted
       </Directory>
</VirtualHost>

<VirtualHost *:80>
        ServerName backend.dev.myapp.com
        DocumentRoot /home/matt/website/api
        <Directory /home/matt/website/api>
        Options Indexes FollowSymLinks
        AllowOverride None
        Require all granted
        </Directory>
        Header set Access-Control-Allow-Origin "frontend.dev.myapp.com"
        ProxyPreserveHost On
        ProxyPass / http://<server_ipaddress>:8800/
        ProxyPassReverse / http://<server_ipaddress>:8800/
</VirtualHost>

(note I’ve also added a line Header set Access-Control-Allow-Origin "frontend.dev.myapp.com" to the backend, this is important, without it it won’t work)

Now configure your frontend to use backend.dev.myapp.com

Once that’s all done, restart Apache and point your browser at http://frontend.dev.myapp.com :slight_smile:

2 Likes

Thanks for your help.

I added 127.0.0.1 frontend.dev.myapp.com backend.dev.my.app.com to the /etc/hosts and changed the VirtualHost .conf file exactly as you had written but when I tried to restart apache with “sudo service apache2 restart”

I got this:

Job for apache2.service failed because the control process exited with error code.
See "systemctl status apache2.service" and "journalctl -xeu apache2.service" for details.

systemctl status apache2.service had this:

apachectl[1315]: AH00526: Syntax error on line 19 of /etc/apache2/sites-enabled/mjna.conf:
apachectl[1315]: Invalid command 'Header', perhaps misspelled or defined by a module not included in the server configuration

And journalctl -xeu apache2.service had the same. The error.log had this:

[mpm_prefork:notice] [pid 762] AH00169: caught SIGTERM, shutting down

I checked the spelling in the .conf file and everything is how you wrote it. Any idea what might be causing this error?

You probably don’t have the headers module enabled yet.

Try running sudo a2enmod headers and then restarting apache again.

Thanks. That fixed the problem. But I’m not sure what you mean when you say " configure your frontend to use backend.dev.myapp.com"

Right now when I go to frontend.dev.myapp.com in the browser it says site can’t be reached but if I type my server’s ip_address it will load the frontend the same as before.

To configure my frontend to use backend do you mean with the axios functions and the url’s im using for example:

const authLogin = async(inputs) =>{
        const res = await axios.post("/api/auth/login", inputs);
        setCurrentUser(res.data);

Would I also need to change my backend’s index.js file? For example, for the listen function:

app.listen(8800, ()=>{
    console.log("Connected")
})

Or for my backend’s middleware?

app.use("/api/auth", authRoutes)

As you can tell server configuration is something I’m not familiar with

That is not true. You can host any number of websites on a single ip. You just need to configure Apache appropriately. I have a VPS with a single IP address that I run numerous websites on.

You can, but not if not if you bind two virtual servers to bind to the same IP only. That’s what I meant. I could have phrased it better.

I was assuming Apache was running on your local machine. If it isn’t you shut put your server’s IP in /etc/hosts rather than 127.0.0.1

Yes, though it’s probably easiest to configure Axios’ baseUrl:

Axios.defaults.baseURL = "http://backend.dev.myapp.com/";
1 Like

I changed /etc/hosts as follows:

127.0.0.1 localhost
127.0.1.1 donald-driver

# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

<server_ipaddress> frontend.dev.myapp.com backend.dev.myapp.com

But the same problem persists. frontend.dev.myapp.com says site can’t be found and http://<server_ipaddress> loads the landing page of my frontend which can’t connect to the backend.

I put axios.defaults.baseURL into the frontend’s app.js file before doing a production build. Is this the correct place to put it?