How to restrict backend to only serve requests from my Netlify site

Not a foolproof method, but you can check for referrer. If referrer contains your Netlify domain, allow the function to be executed, or reject it.

The referrer can easily be spoofed. Is there no other way?

Not that I know of. If you find one, it would help me and many others greatly.

Generally, you can whitelist the IP of your frontend application. Is there no way to automatically add a rotated secret key to the API requests made by the frontend?

A support engineer can actually answer if that’s possible. So, I’ve escalated this thread, they’ll respond shortly.

@hrishikesh Checking in- any update here?

@ankitgoyal100, I’m not sure how this thread was missed, but you can expect a reply soon enough. I’m tagging @luke, @jen, @perry for more attention.

Not possible to allow-list IPs, I’m afraid, given the sophisticated nature of our traffic management and routing, and dynamic nature of our points-of-presence.

Depending what the backend is, you may want to consider some sort of auth. You could, for example, make the request via a Netlify Function and protect the endpoint using basic-auth headers which are maintained in a _headers rule, or JWTs.

In a personal capacity, I’ve gated function calls with basic-auth headers with great success! But, these requests will all be served by our origin server (whereas JWT-driven auth is served by each CDN endpoint).

That’s my 2c!

@Scott Thank you, that’s very helpful. Our backed is in Django and we have portions of our site that are hidden behind an auth screen (using JWT tokens) and other portions of our site that are public. I would like to secure the portions of the site that are public so that our API knows which requests are coming from our site versus someone else who is posing as us.

How would you use a Netlify Function with a Create-React-App?

How does the Netlify Function know that any incoming API requests are coming from my site, and not any malicious actors who are trying to impersonate our site?

I think, through a combination of auth and referrer headers, you’ll have a very good idea that the request is made from your site. In pseudo-code, you can do something like:

if the request uses a referrer value containing mydomain.com, then if the correct auth headers are provided, then execute the function, else it should return a 400 error.

@Scott Referrer headers can be spoofed easily. Also, I have some endpoints hidden behind auth headers and other endpoints that are not hidden behind auth headers because users don’t need to be logged in to see certain data / portions of our application. How do you recommend we serve the endpoints that are not hidden behind auth headers?

@ankitgoyal, I’m currently working on a solution that might address this issue and hopefully it will help many more people.

Here’s what my plan is:

  1. Create a new user in Netlify Identify.
  2. Log the user in.
  3. Get the authorization token and send it to call the serverless function.
  4. Verify if the auth token is valid in the function.
  5. Process the function and delete the user.

This is definitely a lot of work and I’m not sure if it’ll work or if it’s practical at all. But, for anonymous calls to serverless functions, I don’t think there can be any other way.

1 Like

That’s a great idea. Let me know if you’re able to get it to work–I’m not worried about doing a bit of extra work. I’d much prefer to have a stable, secure solution in place!

Great news! Seems like it worked. My testing was fairly limited though and I might have missed something. Let me know if something doesn’t work.

Side note: I’m sorry I am not sharing this as a repo for now as I implemented this in a project I already have. I can link to its repo, but you’d have to find the relevant code which would be a waste. I could setup another repo, but I was excited to share the solution promptly.

So, here you go:

First run:

npm i gotrue-js node-fetch

In your client-side JS:

import GoTrue from 'gotrue-js' // This added around 15 KB to my bundle

let auth = new GoTrue({

  APIUrl: 'https://website.netlify.app/.netlify/identity'

  /*
    The above URL is also displayed in the Identity tab of the website. I suppose it's safe to keep the URL
    in the client-side code because it's a non-customizable URL provided by Netlify. So, probably anyone
    can guess the URL. For example, https://www.website.com/ will have it at 
    https://www.website.com/.netlify/identity.
    I sincerely hope Netlify is able to control who can connect to this URL.
  */

})

/*
  Create any random email and password as you are anyways going to delete the user. It's recommend 
  that you generate a random email, (if not password), for each function call so that if multiple users try
  to call the function at the same time, it won't return 'user already exists' error
*/

auth.signup(email, password).then(() => {
  auth.login(email, password).then(response => {
    fetch('/.netlify/functions/functionName/', {
      headers: {
        Authorization: 'Bearer ' + response.token.access_token
      }
    }).then(/*carry on*/)
  }
}

In your serverless function:

const fetch = require('node-fetch')

exports.handler = async (event, context) => {

  const {identity, user} = context.clientContext

  if (user) {

    return fetch(identity.url + '/admin/users/' + user.sub, {
      method: 'DELETE',
      headers: {
        Authorization: 'Bearer ' + identity.token
      }
    }).then(() => {

    // rest of the function here

    })

  } else {

  // show error

  }

}

I have typed the code here directly, so chances are I might have missed some brackets or made a mistake with indention or something. So, try it once and let me know if it works.

Note that, you cannot delete the user from Netlify CLI. So, to test the deletion, you’d have to publish the website.

@hrishikesh Thank you so much! I’ll give it a shot shortly. So, once I create a random email/password and then DELETE the user, how does my backend know that the frontend is fully authenticated and that the request is coming from a “trusted” source? Should I not delete the user, and instead have my backend verify the user identity in the Netlify function on every API request?

I hope by trusted source you mean your website.

When you pass the Authorization header which is the access token of the logged in user, the serverless function will try to decode it. This is what is being stored here: const {identity, user} = context.clientContext

So, when the access token is a valid one, the user object is not null, and thus, the if statement runs. If the access token is not a valid one, the user object would be null, thus the condition will fail.

Since the function in question is supposed to be called anonymously, we don’t want to tell the user to create an account. Thus, we create one ourselves with a random email + password. Since the user will have a unique ID and only be valid for your website, the function would know that the request is coming from your website.

I don’t think if I’ve explained this in the easiest way but I really hope it makes some sense. Let me know if you have any further questions.

If you don’t delete the user, well you’d have to make sure to generate an email that’s not used before. Also, it will clutter your Identity users list real soon and seems unnecessary.


Also, I forgot to mention in my previous post, the 2 most important settings for Identity are:

  1. Registrations should be open:

  1. Auto confirm should be on:

@hrishikesh That all makes very good sense, thank you for explaining!

Since the function in question is supposed to be called anonymously, we don’t want to tell the user to create an account. Thus, we create one ourselves with a random email + password. Since the user will have a unique ID and only be valid for your website, the function would know that the request is coming from your website.

It makes sense that the function knows the request is coming from my website. I have a separate backend service (written in Django Rest Framework)–how does my backend service communicate with the Netlify function to get the data that this user is a “validated user”? My backend service needs to know that the user is a “validated user” so it can make certain APIs available. Does that make sense?

Well, you can call your backed from within the function after you check if the user is valid. Is this possible for you?

Sadly, this “security” model will only work with Netlify function (I suppose) because decoding the auth token might not be possible for other services.

@hrishikesh Yes I can definitely call my backend from within the function. I can have my backend generate a short lived token (that is only valid for the user’s session) and use that temporary token to authenticate APIs that non-logged users should be able to access. Does that sound sufficient and secure?

Yes seems like that should do. You can try visiting your function by directly calling the URL, or by sending an invalid auth token on purpose to see if it’s properly showing the error. As long as it’s doing that, I think you’re fine.