Search code examples
javascriptfirebasegoogle-cloud-functionsfirebase-hosting

Issue returning 404 with Cloud Functions on Firebase Hosting


I am having trouble handling a (custom) 404 with Firebase Hosting and Functions. Below is a simplified code that works without issue on localhost.

However there seem to be an issue handling 404 when I deploy it. Firebase Hosting returns a 500 instead of 404.

  • https://xxxx.web.app/myfunction/hello -> returns "hello world"
  • https://xxxx.web.app/myfunction/404 -> returns 500 error
  • http://localhost/myfunction/404 -> returns (custom) 404

Does anyone know what is wrong with this?

index.js

const functions = require('firebase-functions');

exports.myfunction = functions.region('asia-east1').runWith({maxInstances: 2}).https.onRequest((request, response) => {
    if (request.path == '/myfunction/hello') {
        return response.send("hello world")
    }

    response.set('X-Cascade', 'PASS');
    return response.status(404).end()
});

firebase.json

{
  "functions": [
    {
      "source": "functions",
      "codebase": "default",
      "ignore": [
        "node_modules",
        ".git",
        "firebase-debug.log",
        "firebase-debug.*.log"
      ]
    }
  ],
  "hosting": {
    "public": "public",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "/myfunction/*",
        "function": "myfunction",
        "region": "asia-east1"
      }
    ]
  }
}

edit: The basic file structure is something like this:

.
├── firebase.json
├── functions
│   ├── index.js
│   └── package.json
└── public
    ├── 404.html
    └── index.html

Solution

  • Firebase functions and firebase hosting run on completely different environments when in production, therefore the 404.html file in the public folder will only apply when the browser triggers a 404 Not Found error, NOT a firebase function which is on the server side as seen from the documentation.

    This is different when you are using the local emulator, because the functions emulator has access to the files, hence why it is working locally.

    From your code, you have two options.

    Option 1

    You can create a custom 404.html inside the functions directory and send it to the browser as shown below.

    NB: I have removed X-Cascade because is not necessary and will cause a 500 error as you described.

    const functions = require('firebase-functions');
     
     exports.myfunction = functions
       .region('asia-east1')
       .runWith({ maxInstances: 2 })
       .https.onRequest((request, response) => {
         if (request.path == '/myfunction/hello') {
           return response.send('hello world');
         }
     
         return response.status(404).sendFile('404.html', { root: __dirname });
       });
    

    Option 2

    A simpler way is to specify the route in your firebase.json.

      "hosting": {
        "public": "public",
        "ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
        "rewrites": [
          {
            "source": "/myfunction/hello",
            "function": "myfunction",
            "region": "asia-east1"
          }
        ],
      },
    
    

    Then in your cloud function:

    const functions = require('firebase-functions');
    
    exports.myfunction = functions
      .region('asia-east1')
      .runWith({ maxInstances: 2 })
      .https.onRequest((request, response) => {
        return response.send('hello world');
      });