Email subscription stopped working after Ghost update

I updated Ghost from v5.28.0 to v5.75.1 and my homepage email subscription form stopped working. Running v5.28.0 I have disabled all the Portal scripts and added a custom form, which worked fine until the recent update.

To doublecheck if my Mailgun configuration is ok after Ghost update I sent a test newsletter to my email. I have got that published article delivered. Which means things should be fine on the Mailgun side.

On email submission I have this error in the browser console: Failed to load resource: the server responded with a status of 429 ()

Following Magic link URL I get this browser output: {"errors":[{"message":"Resource not found","context":null,"type":"NotFoundError","details":null,"property":null,"help":null,"code":null,"id":"e52269c0-9f4d-11ee-a260-432357601017","ghostErrorCode":null}]}

Did v5.75.1 changed something I can no longer contact “/members/api/send-magic-link/” endpoint? How do I troubleshoot this issue? Here is the JS of my form:

async function sendMagicLink(email) {
  try {
    const body = JSON.stringify({
      email: email,
      emailType: "signup",
      urlHistory: [],
    });

    const response = await fetch('/members/api/send-magic-link/', {
      headers: {
        'Content-Type': 'application/json',
      },
      body: body,
      method: 'POST',
    });

    const formContainer = document.querySelector('.subscribe');
    formContainer.classList.remove('error');
    formContainer.classList.add('success');
  } catch (error) {
    const formContainer = document.querySelector('.subscribe');
    formContainer.classList.remove('success');
    formContainer.classList.add('error');
  }
}

document.addEventListener('DOMContentLoaded', function () {
  const form = document.querySelector('.data-members-form');
  if (form) {
    form.addEventListener('submit', async function (e) {
      if (e) {
        e.preventDefault();
        const emailInput = document.querySelector('input[data-members-email]');

        if (!validateEmail(emailInput.value)) {
          const formContainer = document.querySelector('.subscribe');
          formContainer.classList.remove('success');
          formContainer.classList.add('error');
          return;
        }

        await sendMagicLink(emailInput.value);
      }
    });
  }
});

function validateEmail(email) {
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return emailRegex.test(email);
}

Error 429 is usually a rate limit – so, this might be subjective to you, since you probably tested it quite a few times. At some point Ghost locks you out for too many attempts. Which…is actually a good sign. It means your API requests actually “arrive”.

When I go to your website and try the form, I can see that the request is sent to your server, but it times out (also indicated by the 504 error, which represents a gateway timeout).

What I noticed is that the form shows a “success” message, even though an error is returned – but that’s just a side note here, I guess :wink:

As far as I am aware, there haven’t been any significant changes lately that should impact this behavior. I also just checked on my own Ghost website and the API link for the magic link is the same (and it works on my end).

Have you tried restarting your Ghost server, by any chance? What do the server logs say? Does the request actually arrive on the server?

PS: opening the API route in the browser returns a “Resource not found” because you’re basically throwing a GET request at it, while it expects a POST request (which you also have in your script).

Thank you for such a thorough explanation!

I did restart Ghost multiple times. Here is a part of my log that might be related to the issue:

{
  "name": "Log",
  "hostname": "Ghost",
  "pid": 6375,
  "level": 50,
  "version": "5.75.1",
  "err": {
    "id": "02eb1ec0-9846-11ee-9924-4b44f12bd527",
    "domain": "https://www.my_domain.com",
    "code": null,
    "name": "EmailError",
    "statusCode": 500,
    "level": "normal",
    "message": "Failed to send email. Reason: Email has been temporarily rejected.",
    "context": "\"Unable to send welcome email, your site will continue to function.\"",
    "help": "\"Please see https://ghost.org/docs/config/#mail for instructions on configuring email.\"",
    "stack": "EmailError: Failed to send email. Reason: Email has been temporarily rejected.\n    at createMailError (/var/www/ghost/versions/5.75.1/core/server/services/mail/GhostMailer.js:105:12)\n    at GhostMailer.handleDirectTransportResponse (/var/www/ghost/versions/5.75.1/core/server/services/mail/GhostMailer.js:195:19)\n    at GhostMailer.send (/var/www/ghost/versions/5.75.1/core/server/services/mail/GhostMailer.js:157:25)\n    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n    at async /var/www/ghost/versions/5.75.1/core/server/services/auth/setup.js:186:17",
    "hideStack": false
  },
  "msg": "Failed to send email. Reason: Email has been temporarily rejected.",
  "time": "2023-12-11T16:54:55.923Z",
  "v": 0
}

I suppose it says something is wrong with my email configuration. But I transferred email config from the old blog using “Export your content” function. Moreover, I’m able to send published posts in a newsletter.

I don’t think content export & import actually moves transactional email configuration.

The fact that newsletters work is good news, but they get sent entirely differently (via mailgun), so that’s not diagnostic.

Can you please post the mail part your config.json file (or the equivalent if you have a docker compose file), obfuscating any passwords and API keys? My guess is that your transactional email is messed up.

Some info on your Ghost setup (vps/cloud platform somewhere/docker/etc) and how you /think/ transactional email should be set up to deliver may be helpful, too.

2 Likes

You are right, I have to modify email config in config.production.json after switching to a new VPS! I updated missing configuration and it works now.

1 Like