如何在 Vercel 上正确发送邮件(解决本地正常但部署后静默失败的问题)

发布时间 - 2026-01-07 00:00:00    点击率:

vercel 默认 edge 运行时限制了 node.js 原生网络模块的使用,导致 resend/nodemailer 等邮件客户端在部署后无报错、无响应。需显式声明 `runtime: 'nodejs'` 并启用动态路由,才能确保邮件 api 正常调用。

在 Vercel 上发送邮件失败(如日志停在 console.log('subject', subject) 后不再继续),并非因免费账户限制——Vercel 本身不提供 SMTP 服务,也不拦截邮件请求;真正原因是其默认运行时行为:

Edge Functions(默认):基于 WebAssembly 的轻量运行时,不支持 node:net、node:tls 等底层模块,而 Resend SDK(v3+)和绝大多数邮件库(包括 Nodemailer)底层依赖这些模块发起 HTTPS 请求。即使使用 fetch,Edge 环境对 Content-Type、重定向、证书验证等也有更严格限制,易导致静默挂起或连接超时。

Node.js Serverless Functions(需显式启用):完整兼容 CommonJS/ESM、原生 Node 模块及标准 https/http 客户端,是调用 Resend、SendGrid、Nodemailer 等服务的唯一可靠选择

✅ 正确配置方式(以 Resend 为例)

在你的 API 路由文件(如 app/api/send/route.ts 或 pages/api/send.ts)顶部添加:

// app/api/send/route.ts
export const runtime = 'nodejs'; // 关键:强制使用 Node.js 运行时
export const dynamic = 'force-dynamic'; // 确保每次请求都执行,避免缓存干扰

import { Resend } from 'resend';
import { NextResponse } from 'next/server';

export async function POST(request: Request) {
  try {
    const { to, subject, html } = await request.json();

    console.log('sendEmail - start');
    console.log('to:', to);
    console.log('subject:', subject);

    const resend = new Resend(process.env.RESEND_API_KEY!);
    const response = await resend.emails.send({
      from: process.env.SMTP_FROM_EMAIL!,
      to,
      subject,
      html,
    });

    console.log('Resend success:', response);
    return NextResponse.json({ success: true, data: response });
  } catch (error) {
    console.error('Email send error:', error);
    return NextResponse.json(
      { success: false, error: (error as Error).message },
      { status: 500 }
    );
  }
}
⚠️ 注意事项:runtime = 'edge' ❌ 是错误方案(官方文档旧示例已过时);Edge 下 Resend v3+ 会因缺少 node:crypto/node:stream 等模块直接抛 ReferenceError,但 Vercel 日志可能截断错误堆栈,造成“无输出”假象。环境变量必须在 Vercel 项目设置中配置(Settings → Environment Variables),且不能仅在本地 .env 中定义;RESEND_API_KEY 和 SMTP_FROM_EMAIL 需标记为 Production 作用域。若使用 pages/api 路由,请确保导出 config 对象(Next.js 13–14 兼容写法):export const config = { runtime: 'nodejs' };

? 验证是否生效

部署后访问 Vercel 日志(Dashboard → Your Project → Logs),成功调用应显示完整日志链:

sendEmail - start  
to: user@example.com  
subject: Test Email  
Resend success: { id: "xxx", ... }  

若仍卡住,请检查:

  • 是否误将 runtime 写在 async function 内部(必须为顶层导出);
  • RESEND_API_KEY 是否拼写错误或未保存到 Vercel 后台;
  • HTML 内容是否含非法字符(如未转义

✅ 总结

Vercel 不限制邮件发送功能本身,也无需升级 Pro 计划——关键在于匹配运行时环境。只要将函数明确指定为 runtime: 'nodejs',即可稳定调用 Resend、SendGrid、Mailgun 或自建 SMTP(Nodemailer)。这是 Vercel 官方推荐且生产验证的实践方式。


# nodejs  # html  # js  # node.js  # json  # node  # app  # edge  #   # ai  # 路由  # 环境变量  #  


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 学生网站制作软件,一个12岁的学生写小说,应该去什么样的网站?  魔方云NAT建站如何实现端口转发?  Laravel如何安装Breeze扩展包_Laravel用户注册登录功能快速实现【流程】  大连网站制作费用,大连新青年网站,五年四班里的视频怎样下载啊?  Android使用GridView实现日历的简单功能  简单实现Android验证码  如何挑选高效建站主机与优质域名?  Win11怎么设置默认图片查看器_Windows11照片应用关联设置  怎么用AI帮你为初创公司进行市场定位分析?  再谈Python中的字符串与字符编码(推荐)  Windows10如何删除恢复分区_Win10 Diskpart命令强制删除分区  Android 常见的图片加载框架详细介绍  香港服务器建站指南:外贸独立站搭建与跨境电商配置流程  通义万相免费版怎么用_通义万相免费版使用方法详细指南【教程】  Laravel如何使用Service Provider服务提供者_Laravel依赖注入与容器绑定【深度】  JavaScript如何实现音频处理_Web Audio API如何工作?  Laravel如何使用Passport实现OAuth2?(完整配置步骤)  敲碗10年!Mac系列传将迎来「触控与联网」双革新  最好的网站制作公司,网购哪个网站口碑最好,推荐几个?谢谢?  如何在不使用负向后查找的情况下匹配特定条件前的换行符  Laravel如何理解并使用服务容器(Service Container)_Laravel依赖注入与容器绑定说明  linux写shell需要注意的问题(必看)  iOS中将个别页面强制横屏其他页面竖屏  JavaScript如何实现类型判断_typeof和instanceof有什么区别  javascript如何操作浏览器历史记录_怎样实现无刷新导航  Laravel如何处理文件下载请求?(Response示例)  php结合redis实现高并发下的抢购、秒杀功能的实例  百度浏览器网页无法复制文字怎么办 百度浏览器复制修复  韩国代理服务器如何选?解析IP设置技巧与跨境访问优化指南  Internet Explorer官网直接进入 IE浏览器在线体验版网址  google浏览器怎么清理缓存_谷歌浏览器清除缓存加速详细步骤  js代码实现下拉菜单【推荐】  Laravel如何实现事件和监听器?(Event & Listener实战)  制作电商网页,电商供应链怎么做?  android nfc常用标签读取总结  如何在宝塔面板中创建新站点?  Chrome浏览器标签页分组怎么用_谷歌浏览器整理标签页技巧【效率】  Laravel如何生成URL和重定向?(路由助手函数)  制作网站软件推荐手机版,如何制作属于自己的手机网站app应用?  无锡营销型网站制作公司,无锡网选车牌流程?  Win11怎么更改系统语言为中文_Windows11安装语言包并设为显示语言  JavaScript常见的五种数组去重的方式  Python文件操作最佳实践_稳定性说明【指导】  node.js报错:Cannot find module 'ejs'的解决办法  Swift中循环语句中的转移语句 break 和 continue  如何用5美元大硬盘VPS安全高效搭建个人网站?  PHP的CURL方法curl_setopt()函数案例介绍(抓取网页,POST数据)  什么是javascript作用域_全局和局部作用域有什么区别?  java获取注册ip实例  Laravel如何使用Eloquent ORM进行数据库操作?(CRUD示例)