Not able to send mails using SMTP configuration
We have been provided with the following details for SMTP setup.
- Host: abc.xyz.com
- Port: 25
- TLS: STARTTLS supported (TLS 1.2/1.3)
- Auth: None - we have allowlist 172.193.80.67
I’ve followed this article : https://experienceleague.adobe.com/en/docs/experience-manager-learn/cloud-service/networking/examples/email-service and setup the portForwards properly with portOrigin being 30002 and port destination being 25. Also put in the host properly.

Also, since the requirement is for dedicated egress IP address we have also enabled the same.

This is how the com.day.cq.mailer.DefaultMailService.cfg.json looks like :
{ "smtp.host": "$[env:AEM_PROXY_HOST;default=proxy.tunnel]", "smtp.port": "30002", "from.address": "aem-noreply@xyz.com", "smtp.ssl": false, "smtp.starttls": true, "smtp.requiretls": false, "debug.email": false, "oauth.flow": false}Now using a servlet, I’m trying to send an email to a user in order to check if the connection is successful.
Getting error as :
24.07.2026 03:09:51.295 [cm-p155316-e1648032-aem-author-5f9988c66f-t92pq] *ERROR* [20.44.198.33 [1784862591277] GET /bin/sendemail HTTP/1.1] com.linkedinaemguides.core.servlets.SendEmailServlet SendEmailServlet: Unexpected exception while sending email. Recipient=[arichakravorty@xyz.com]
com.day.cq.mailer.MailingException: org.apache.commons.mail.EmailException: Sending the email to the following server failed : proxy.tunnel:30002
Caused by: org.apache.commons.mail.EmailException: Sending the email to the following server failed : proxy.tunnel:30002
at org.apache.commons.mail.Email.sendMimeMessage(Email.java:1298) [org.apache.commons.commons-email:1.6.0]
at org.apache.commons.mail.Email.send(Email.java:1282) [org.apache.commons.commons-email:1.6.0]
at com.day.cq.mailer.impl.DefaultMailService.send(DefaultMailService.java:365) [com.day.cq.cq-mailer:5.15.10]
... 240 common frames omitted
Caused by: javax.mail.MessagingException: Could not connect to SMTP host: proxy.tunnel, port: 30002, response: -1
at com.sun.mail.smtp.SMTPTransport.openServer(SMTPTransport.java:2197) [com.sun.mail.javax.mail:1.6.2]
at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:740) [com.sun.mail.javax.mail:1.6.2]
at javax.mail.Service.connect(Service.java:366) [com.sun.mail.javax.mail:1.6.2]
at javax.mail.Service.connect(Service.java:246) [com.sun.mail.javax.mail:1.6.2]
at javax.mail.Service.connect(Service.java:195) [com.sun.mail.javax.mail:1.6.2]
at javax.mail.Transport.send0(Transport.java:254) [com.sun.mail.javax.mail:1.6.2]
at javax.mail.Transport.send(Transport.java:124) [com.sun.mail.javax.mail:1.6.2]
at org.apache.commons.mail.Email.sendMimeMessage(Email.java:1295) [org.apache.commons.commons-email:1.6.0]
... 242 common frames omitted
The servlet code is :
package com.xyz.core.servlets;
import com.day.cq.mailer.MessageGatewayService;
import com.day.cq.mailer.MessageGateway;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.SimpleEmail;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.servlets.HttpConstants;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.Servlet;
import java.io.IOException;
@Component(
service = Servlet.class,
property = {
Constants.SERVICE_DESCRIPTION + "=Test Send Email Servlet (OOTB MessageGatewayService)",
"sling.servlet.methods=" + HttpConstants.METHOD_GET,
"sling.servlet.paths=" + "/bin/sendemail"
}
)
public class SendEmailServlet extends SlingAllMethodsServlet {
private static final long serialVersionUID = 1L;
private static final Logger LOG = LoggerFactory.getLogger(SendEmailServlet.class);
private static final String EMAIL_SUBJECT = "Test Email from AEM";
private static final String EMAIL_BODY = "This is a test email sent from a custom Sling servlet using the OOTB MessageGatewayService.";
@Reference
private MessageGatewayService messageGatewayService;
@Override
protected void doGet(final SlingHttpServletRequest req,
final SlingHttpServletResponse resp) throws IOException {
resp.setContentType("application/json;charset=UTF-8");
// Recipient comes from ?to= query param, e.g. /bin/sendemail?to=someone@example.com
String rawRecipient = req.getParameter("to");
String recipient = sanitizeForLog(rawRecipient);
LOG.info("SendEmailServlet: doGet() invoked. Raw 'to' param supplied=[{}]", StringUtils.isNotBlank(rawRecipient));
if (StringUtils.isBlank(rawRecipient)) {
LOG.warn("SendEmailServlet: No 'to' query parameter supplied. Rejecting request.");
writeJsonResponse(resp, SlingHttpServletResponse.SC_BAD_REQUEST,
"Missing required query parameter: to", null);
return;
}
// Very basic sanity check, not full RFC validation
if (!rawRecipient.contains("@") || !rawRecipient.contains(".")) {
LOG.warn("SendEmailServlet: 'to' param does not look like a valid email address. Value=[{}]", recipient);
writeJsonResponse(resp, SlingHttpServletResponse.SC_BAD_REQUEST,
"Invalid email address format", recipient);
return;
}
LOG.info("SendEmailServlet: Preparing to send test email. Recipient=[{}]", recipient);
try {
LOG.debug("SendEmailServlet: Building SimpleEmail object. Subject=[{}]", EMAIL_SUBJECT);
SimpleEmail email = new SimpleEmail();
email.setSubject(EMAIL_SUBJECT);
email.setMsg(EMAIL_BODY);
email.addTo(rawRecipient);
LOG.debug("SendEmailServlet: Email object built successfully. Fetching MessageGateway for SimpleEmail.class");
MessageGateway<SimpleEmail> messageGateway = messageGatewayService.getGateway(SimpleEmail.class);
if (messageGateway == null) {
LOG.error("SendEmailServlet: MessageGateway is null. Recipient=[{}]. Check that " +
"com.day.cq.mailer.DefaultMailService OSGi config is present and valid " +
"(smtp.host, smtp.port, smtp.user, etc.)", recipient);
writeJsonResponse(resp, SlingHttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"MessageGateway not available - check mail service OSGi config", null);
return;
}
LOG.info("SendEmailServlet: MessageGateway obtained. Sending email now. Recipient=[{}]", recipient);
messageGateway.send(email);
LOG.info("SendEmailServlet: Email send() call completed without exception. " +
"Note: this does not guarantee delivery, only that the message was handed off to the mail service. " +
"Recipient=[{}], Subject=[{}]", recipient, EMAIL_SUBJECT);
writeJsonResponse(resp, SlingHttpServletResponse.SC_OK,
"Email dispatched successfully", recipient);
} catch (EmailException e) {
LOG.error("SendEmailServlet: EmailException occurred while building/sending email. Recipient=[{}]",
recipient, e);
writeJsonResponse(resp, SlingHttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Failed to send email", null);
} catch (Exception e) {
LOG.error("SendEmailServlet: Unexpected exception while sending email. Recipient=[{}]",
recipient, e);
writeJsonResponse(resp, SlingHttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Unexpected error while sending email", null);
} finally {
LOG.info("SendEmailServlet: doGet() finished. Recipient=[{}]", recipient);
}
}
/**
* Strips CR/LF and other control characters from user-supplied input before it is
* ever written to logs or HTTP responses, preventing log forging / injection.
*/
private String sanitizeForLog(String input) {
if (input == null) {
return "";
}
return input.replaceAll("[\\r\\n\\t]", "_").trim();
}
/**
* Writes a small JSON response without manual string concatenation of
* externally-controlled values.
*/
private void writeJsonResponse(SlingHttpServletResponse resp, int status, String message, String detail) throws IOException {
resp.setStatus(status);
org.json.JSONObject json = new org.json.JSONObject();
json.put("message", message);
if (detail != null) {
json.put("detail", detail);
}
resp.getWriter().print(json.toString());
}
}
Can I please get some help to resolve this issue or to figure out as to why this is happening?
Note: 172.193.80.67 is the dedicated egress IP address and the same is already whitelisted.