When Outlook and Hotmail reject your mail server: error S3150

Someone wrote to one of my addresses from an msn.com account. I answered from my mail program, through the mail server I run on a rented virtual machine. A minute later the answer was back in my inbox:
550 5.7.1 Unfortunately, messages from [203.0.113.10] weren't sent.
Please contact your Internet service provider since part of their
network is on our block list (S3150).
The server sends mail every day. SPF and DKIM pass, and other providers accept its mail without a murmur. Only Microsoft's consumer mailboxes, which means outlook.com, hotmail.com, live.com and msn.com, said no.
Reading the bounce
The important words are "part of their network". Microsoft is not saying my server sent spam, and it is not saying my authentication is broken. It is saying that a range of addresses belonging to my hosting provider is on its block list, and my server's address is inside that range.
The code says the same. S3150 is the code Microsoft attaches to this sentence about a blocked part of a network. A message that fails authentication looks different and mentions SPF, DKIM or DMARC by name.
That distinction decides what is worth doing next. With a content or authentication problem, you fix your side and try again. With a range block there is nothing on your side to fix. Somebody else on the same provider, in the same address block, sent enough junk that Microsoft stopped listening to the whole street.
It is worth confirming that it really is only this. On the server, the mail log shows every rejection with its code:
grep -c "S3150" /var/log/exim_mainlog
grep "550 5.7" /var/log/exim_mainlog | tail -20
In my case there was exactly one S3150 in the log since the start of the month, and no other 5.7 rejections. So it was a single recipient at a single provider, hit by a block I had no part in.
What does not help
Changing the mail. The message never got far enough to be read. Rewording it, removing the signature or the link changes nothing.
Tightening SPF or DKIM. They were passing. A range block is applied before any of that is looked at.
Waiting. Nobody tells you how long a range stays blocked. The person waiting for my answer should not have to find out.
Routing all mail of the server through a relay. This is the first idea everyone has: configure the server to hand all outgoing mail to a sending service. I looked at it and left it alone. The server carries mailboxes for several domains. A sending service only accepts mail from domains you have verified with it, so every one of those domains would need to be set up there first, or their mail would start failing instead. That is a real migration, with real risk, to solve one bounced reply.
The detour for one mail
One of the domains on that server was already verified with Amazon SES, because the site behind it sends its sign-up confirmations that way. So the address I was answering from could legitimately send through SES. SES sends from Amazon's address ranges, which Microsoft accepts.
I saved the reply from my mail program as a file, which gives a complete raw message with all headers, and handed it to SES from the terminal with the AWS command line, version 2:
aws ses send-raw-email \
--region us-east-1 \
--raw-message "Data=$(base64 -i reply.eml)"
The access key I used belongs to a user who is allowed to send mail and nothing else, the same one the site uses. The raw message already contains the From, To, Subject and the reply headers, so the answer threads correctly in the recipient's mailbox.
Before sending it to the real recipient, I sent the same file to an address of my own on the old server, to see what a receiving side makes of it. It arrived, the spam filter gave it a neutral score, and the return path showed the bounce subdomain I had set up for SES. With that confirmed, the reply itself can go the same way.
Two things to know about this route. First, a mail sent this way does not appear in the Sent folder of your mail program, because your mail program did not send it. Add your own address as a blind copy in the raw file, or you will have no record of what you answered. Second, any reply comes back to your normal mailbox as always. Only the outgoing leg took the detour.
If it keeps happening
One bounce is an incident. If it becomes a pattern, there are two proper fixes.
The first is asking Microsoft to remove the block for your address. There is a form for senders, it is answered by people, and for a single clean address in a blocked range it is sometimes granted. It takes days, and it can be undone by the next bad neighbour.
The second is making the detour permanent for the one mailbox that needs it. SES offers an SMTP endpoint with its own credentials. Enter it as the outgoing server for that account in your mail program, and every mail you write from that address leaves through SES, while incoming mail stays where it is. That touches one mailbox on one domain and leaves the rest of the server alone.
I have done neither yet. One bounce in a month does not justify either.
What I took from it
Running your own mail server means your reputation is partly your neighbours'. You can do everything right and still be turned away because of the address block your provider put you in. This says nothing against self-hosting. It does mean you need a second way out that is ready before you need it.
For me that second way is a sending service where the domain is already verified, a key that can only send, and the one command above in a note. The next time a bounce says S3150, the detour takes two minutes.
