/.netlify/functions/myscript.js : Error 502 on Safari, Works fine on Chrome

PLEASE help us help you by writing a good post!

Site : camping-arolla-2018.netlify.app
Dev branch : https://function-stripe-v2--camping-arolla-2018.netlify.app/shop/

  • I followed the (brillant one, thnaks) tutorial you made to integrate Stripe with Netlify.

  • All works fine, and after adapting some design things, I have a working site (dev) made with Hugo and Netlify Functions. Perfect. I can order what I want. Cool.

  • But I was dev on Chrome/Brave.

  • When I try to test with safari, all if fine, except that I get an error when the https://function-stripe-v2--camping-arolla-2018.netlify.app/.netlify/functions/create-checkout is called.

  • The error is an error 502 witch is a “bad proxy” error. So it seems on the Netlify side.

This is the detailed error in have in Safari dev tools. Again all works fine with Chrome.

I’m trying to debug this thing for hours. Starting to getting nuts :slight_smile:
Any idea ? Found nothing similar on the forum.

Header is:

URL: https://function-stripe-v2--camping-arolla-2018.netlify.app/.netlify/functions/create-checkout
État: 502
Source: Réseau

:method: POST
:scheme: https
:authority: function-stripe-v2--camping-arolla-2018.netlify.app
:path: /.netlify/functions/create-checkout
Cookie: __stripe_mid=78d3cca6-1bd8-49ac-b139-85c0c8bda2240ee215; __stripe_sid=44010473-fa4a-4d67-8cf6-6cbfe1cdd868e0c689; displayCookieConsent=y
Accept: */*
Content-Type: application/json
Origin: https://function-stripe-v2--camping-arolla-2018.netlify.app
Accept-Encoding: br, gzip, deflate
Host: function-stripe-v2--camping-arolla-2018.netlify.app
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15
Content-Length: 33
Accept-Language: fr-fr
Connection: keep-alive

:status: 502
Content-Type: text/plain; charset=utf-8
Age: 1
Date: Tue, 01 Sep 2020 12:13:34 GMT
Content-Length: 621
Cache-Control: no-cache
Server: Netlify
x-nf-request-id: 991cff8b-b98c-4440-ac2c-c0b7b6c29d13-28898704

Données de la demande
Type MIME: application/json
Données de la demande:  {"sku":"CARTE-01","quantity":"1"}

Response is :

    "errorType": "Error",
    "errorMessage": "Invalid URL: undefined/undefined/thanks/. URLs must begin with http or https.",
    "trace": [
        "Error: Invalid URL: undefined/undefined/thanks/. URLs must begin with http or https.",
        "    at Function.generate (/var/task/src/node_modules/stripe/lib/Error.js:39:16)",
        "    at IncomingMessage.<anonymous> (/var/task/src/node_modules/stripe/lib/StripeResource.js:190:33)",
        "    at Object.onceWrapper (events.js:421:28)",
        "    at IncomingMessage.emit (events.js:327:22)",
        "    at endReadableNT (_stream_readable.js:1221:12)",
        "    at processTicksAndRejections (internal/process/task_queues.js:84:21)"

My 2 scripts are:


// Stripe secret Key on Netlify ENV Variable
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
// The inventory (Only one language at the moment)
const inventory = require('./data/products.json');

exports.handler = async (event) => {
   // get the mandatory information for charging the client : sku + quantity
   //   The following URLs are set according to form language
   //   Get language (formlang) transmited from the form
   //   Get Absolute path for images on the Stripe checkout page
   const { sku, quantity, formlang, stripeImgPath, stripeImg } = JSON.parse(event.body);
   // Find the product
   const product = inventory.find((p) => p.sku === sku);
   // Sanitize quantity
   const validateQuantity = quantity > 0 && quantity < 11 ? quantity : 1;

   function root(formlang) {
      let rootUrl;
      if (formlang == "fr") {
         rootUrl = ``;
      } else {
         rootUrl = `/`+formlang;
      return rootUrl;
   // Get the return URL part for the used language
   const rootUrl = root(formlang);

   // Create Stripe session
   const session = await stripe.checkout.sessions.create({
      payment_method_types: ['card'],
      billing_address_collection: 'auto',
      shipping_address_collection: {
         // All available countries Worldwide
         allowed_countries: ['AC','AD','AE','AF','AG','AI','AL','AM','AO','AQ','AR','AT','AU','AW','AX','AZ','BA','BB','BD','BE','BF','BG','BH','BI','BJ','BL','BM','BN','BO','BQ','BR','BS','BT','BV','BW','BY','BZ','CA','CD','CF','CG','CH','CI','CK','CL','CM','CN','CO','CR','CV','CW','CY','CZ','DE','DJ','DK','DM','DO','DZ','EC','EE','EG','EH','ER','ES','ET','FI','FJ','FK','FO','FR','GA','GB','GD','GE','GF','GG','GH','GI','GL','GM','GN','GP','GQ','GR','GS','GT','GU','GW','GY','HK','HN','HR','HT','HU','ID','IE','IL','IM','IN','IO','IQ','IS','IT','JE','JM','JO','JP','KE','KG','KH','KI','KM','KN','KR','KW','KY','KZ','LA','LB','LC','LI','LK','LR','LS','LT','LU','LV','LY','MA','MC','MD','ME','MF','MG','MK','ML','MM','MN','MO','MQ','MR','MS','MT','MU','MV','MW','MX','MY','MZ','NA','NC','NE','NG','NI','NL','NO','NP','NR','NU','NZ','OM','PA','PE','PF','PG','PH','PK','PL','PM','PN','PR','PS','PT','PY','QA','RE','RO','RS','RU','RW','SA','SB','SC','SE','SG','SH','SI','SJ','SK','SL','SM','SN','SO','SR','SS','ST','SV','SX','SZ','TA','TC','TD','TF','TG','TH','TJ','TK','TL','TM','TN','TO','TR','TT','TV','TW','TZ','UA','UG','US','UY','UZ','VA','VC','VE','VG','VN','VU','WF','WS','XK','YE','YT','ZA','ZM','ZW','ZZ']
      // The real next URL for the web site language
      success_url: stripeImgPath+rootUrl+`/thanks/`,
      cancel_url:  stripeImgPath+rootUrl+`/oops/`,

      // Informations about the product
      line_items: [
            name: `[`+product.sku+'] '+product.name ,
            description: product.description,
            images: [stripeImg],
            amount: product.amount,
            currency: product.currency,
            quantity: validateQuantity,

   return {
      statusCode: 200,
      body: JSON.stringify({
         sessionId: session.id,
         publishableKey: process.env.STRIPE_PUBLISHABLE_KEY,



// Handle form the Submit button
export async function handleFormSubmission(event) {

   // Custom Goal for plausible.io

   const form = new FormData(event.target);

   // Get the mandatory data from the form
   //    formlang : for multilanguage sites
   //    stripeImgPath : for local images in STATIC
   //    stripeImg : for local images in ASSETS
   const data = {
      sku: form.get('sku'),
      quantity: form.get('quantity'),
      formlang: form.get('formlang'),
      stripeImgPath: form.get(`stripeImgPath`),
      stripeImg: form.get(`stripeImg`),

   // Create a Stripe Checkout
   const response = await fetch ('/.netlify/functions/create-checkout', {
      method: 'POST',
      headers: {
         'content-type': 'application/json',
      body: JSON.stringify(data),
   }).then((res) => res.json());

   // Manage Checkout response & SessionID
   const stripe = Stripe(response.publishableKey);
   const { error } = await stripe.redirectToCheckout({
      sessionId: response.sessionId,

   if (error) {


looking at your request, you are obviously missing fields formlang, stripeImgPath, stripeImg. Thats why when stripe object is built, it receives undefined in both success_url and cancel_url. Look in devtools to see why data object in stripe-purchase.js doesn’t initialize properly. Also be aware that FormData has some issues in safari: https://developer.mozilla.org/en-US/docs/Web/API/FormData

@Jagh thanks a lot. Ahhh make sense.
But my code (in fact, Netlify lesson code :slight_smile: ) works perfectly on Chrome.

Thanks for the tip about Safari & FormData. I will follow this lead.

Thanks for the lead. Should be a real nasty system bug :slightly_frowning_face:

  • Latest Safari 13.1.2 on Catalina (latest 10.15.6) => works fine
  • Latest Safari 13.1.2 on Mojave (latest 10.14.6) => This bug.

Yes, the same Safari version !!!

I’ll close this issue. Thanks a lot, you avoid me lot of work.

1 Like