Forum Discussion

StefanoC66's avatar
StefanoC66
Iron Contributor
Aug 19, 2025
Solved

O365 hybrid connector to onprem failing TLS

We're having issue with the connector to on-prem from Exchange Online

If we enable the TLS it fails with the error

Cannot connect to remote server [Message=451 5.7.3 STARTTLS is required to send mail

Looking at the on-prem server we noticed that if connecting to port 25 STARTLS is missing but connecting to port 587 is present 

PORT 587

250-SIZE 20971520
250-PIPELINING
250-DSN
250-ENHANCEDSTATUSCODES
250-STARTTLS
250-AUTH NTLM
250-8BITMIME
250-BINARYMIME
250 CHUNKING
451 4.7.0 Timeout waiting for client input

 

PORT 25

250-SIZE 62423040
250-PIPELINING
250-DSN
250-ENHANCEDSTATUSCODES
250-8BITMIME
250-BINARYMIME
250 CHUNKING
451 4.7.0 Timeout waiting for client input

 

Is there a way to have it enabled on port 25 as well ?

 

  • Found that the STARTLS command availability depends on the security options combination enabled on the connector.

    created a connector with just TLS enabled and worked 

7 Replies

  • StefanoC66's avatar
    StefanoC66
    Iron Contributor

    Found that the STARTLS command availability depends on the security options combination enabled on the connector.

    created a connector with just TLS enabled and worked 

  • did you tried with rerun the hybride wizard on the server, this could solve the issue. 

    I will reach out to a former colleague of me at Microsoft maybe he has some ideas.

  • here is an powershell script that may help, be care full it will change certain settings in the connector if you have modified it.

     

    <#
    .SYNOPSIS
      Diagnose (and optionally remediate) common STARTTLS issues on Exchange.
    
    .DESCRIPTION
      - Validates SMTP certificate health & binding to receive connectors.
      - Confirms STARTTLS is advertised on ports 25/587 via EHLO probe.
      - Checks on-prem Send Connector to EXO and (optionally) EXO connectors.
      - Optional -FixTlsBinding will rebind Default Frontend receive connector
        to the active SMTP certificate and restart FrontEndTransport.
    
    .NOTES
      Run in the on-prem Exchange Management Shell as Organization Management.
      For EXO checks, you need the ExchangeOnlineManagement module and a session
      via Connect-ExchangeOnline.
    
    .PARAMETER ExchangeServer
      The on-prem Exchange server name (defaults to local).
    
    .PARAMETER ExpectedCertNames
      CN/SAN values you expect on the SMTP cert (e.g., mail.contoso.com).
    
    .PARAMETER PortsToTest
      Ports to EHLO probe for STARTTLS advertisement (defaults 25,587).
    
    .PARAMETER CheckEXO
      Also check EXO inbound/outbound connectors (requires EXO connection).
    
    .PARAMETER FixTlsBinding
      Rebind Default Frontend receive connector to the active SMTP cert.
    
    .EXAMPLE
      .\Test-ExchangeStartTls.ps1 -ExpectedCertNames mail.contoso.com -CheckEXO
    
    .EXAMPLE
      .\Test-ExchangeStartTls.ps1 -ExpectedCertNames mail.contoso.com -FixTlsBinding
    #>
    
    [CmdletBinding(SupportsShouldProcess)]
    param(
      [string]$ExchangeServer = $env:COMPUTERNAME,
      [string[]]$ExpectedCertNames,
      [int[]]$PortsToTest = @(25,587),
      [switch]$CheckEXO,
      [switch]$FixTlsBinding
    )
    
    # --------- helpers ----------
    $Report = New-Object System.Collections.Generic.List[object]
    function Add-Row([string]$Area,[string]$Item,[string]$Status,[string]$Details,[string]$Recommendation="") {
      $Report.Add([pscustomobject]@{Area=$Area;Item=$Item;Status=$Status;Details=$Details;Recommendation=$Recommendation})
    }
    function OK   { "OK" }
    function WARN { "WARN" }
    function FAIL { "FAIL" }
    
    function Test-EhloStartTls([string]$Host,[int]$Port){
      try{
        $client = New-Object System.Net.Sockets.TcpClient
        $client.ReceiveTimeout = 4000; $client.SendTimeout = 4000
        $client.Connect($Host,$Port)
        $stream = $client.GetStream()
        $reader = New-Object IO.StreamReader($stream)
        $writer = New-Object IO.StreamWriter($stream); $writer.AutoFlush = $true
    
        $banner = $reader.ReadLine()
        $writer.WriteLine("EHLO test.local")
        Start-Sleep -Milliseconds 100
        $lines = @()
        while ($stream.DataAvailable) { $lines += $reader.ReadLine() }
        $writer.WriteLine("QUIT")
        $client.Close()
    
        $hasStartTls = $lines -match "STARTTLS"
        [pscustomobject]@{
          Port        = $Port
          Banner      = $banner
          Lines       = ($lines -join "; ")
          HasSTARTTLS = [bool]$hasStartTls
        }
      } catch {
        [pscustomobject]@{ Port=$Port; Banner=$null; Lines=$_.Exception.Message; HasSTARTTLS=$false }
      }
    }
    
    Write-Host "== STARTTLS diagnostic on $ExchangeServer ==" -ForegroundColor Cyan
    
    # 1) SMTP certificate posture
    try{
      $smtpCerts = Get-ExchangeCertificate -Server $ExchangeServer | Where-Object { $_.Services -match 'SMTP' -and -not $_.IsSelfSigned }
      if(-not $smtpCerts){
        Add-Row "TLS/Cert" "SMTP certificate" (FAIL) "No third-party SMTP certificate enabled on $ExchangeServer" "Install a trusted SMTP cert and enable for SMTP."
      } else {
        foreach($c in $smtpCerts){
          $expired = ($c.NotAfter -lt (Get-Date))
          $nameHit = if($ExpectedCertNames){ @($c.CertificateDomains) | Where-Object { $ExpectedCertNames -contains $_ } } else { $true }
          $status  = if($expired){ FAIL } elseif($nameHit){ OK } else { WARN }
          $rec     = if($expired){ "Renew/replace and Enable-ExchangeCertificate -Services SMTP" }
                     elseif(-not $nameHit){ "Ensure EXO/on-prem connectors use a name present on this cert (CN/SAN)." } else { "No action." }
          Add-Row "TLS/Cert" "SMTP cert $($c.Thumbprint.Substring(0,8))" $status "Domains: $($c.CertificateDomains -join ', '); Expires: $($c.NotAfter)" $rec
        }
      }
    } catch {
      Add-Row "TLS/Cert" "Query certificates" (FAIL) $_.Exception.Message "Run in Exchange Management Shell with proper rights."
    }
    
    # 2) Receive connectors (TLS binding + auth)
    try{
      $rcvs = Get-ReceiveConnector -Server $ExchangeServer | Sort-Object -Property Name
      foreach($r in $rcvs){
        $isFrontend = $r.TransportRole -eq "FrontendTransport"
        $tlsName    = $r.TlsCertificateName
        $auth       = $r.AuthMechanism
        $hasTLS     = ($auth -match "Tls")
        $status     = if($hasTLS -and $isFrontend){ if([string]::IsNullOrWhiteSpace($tlsName)){ WARN } else { OK } } elseif($isFrontend){ WARN } else { OK }
        $rec        = @()
        if($isFrontend -and -not $hasTLS){ $rec += "Enable TLS in AuthMechanism for frontend receive connector." }
        if($isFrontend -and [string]::IsNullOrWhiteSpace($tlsName)){ $rec += "Bind TLS cert via Set-ReceiveConnector -TlsCertificateName '<I>issuer<S>subject'." }
        Add-Row "ReceiveConnector" $r.Identity $status "Auth: $auth; TlsCertificateName: $tlsName" ($rec -join " ")
      }
    } catch {
      Add-Row "ReceiveConnector" "Query" (FAIL) $_.Exception.Message "Check permissions."
    }
    
    # 3) EHLO probe on 25/587 for STARTTLS
    foreach($p in $PortsToTest){
      $probe = Test-EhloStartTls -Host $ExchangeServer -Port $p
      $status = if($probe.HasSTARTTLS){ OK } else { WARN }
      Add-Row "Probe" "EHLO $ExchangeServer:$($probe.Port)" $status "Banner: $($probe.Banner); EHLO: $($probe.Lines)" (if($status -eq "WARN"){"If missing on 25, fix TLS binding on Default Frontend."})
    }
    
    # 4) On-prem Send Connector toward EXO (if any)
    try{
      $send = Get-SendConnector | Where-Object { $_.Name -like "Outbound to Office 365*" -or $_.TlsAuthLevel -ne 'None' }
      if($send){
        foreach($s in $send){
          $hosts = if($s.SmartHosts){ $s.SmartHosts -join "," } else { "DNS (no SmartHosts)" }
          $okTls = $s.RequireTLS -eq $true
          $okEop = ($hosts -match "mail\.protection\.outlook\.com")
          $status = if($okTls){ if($okEop -or $hosts -ne "DNS (no SmartHosts)") { OK } else { WARN } } else { WARN }
          $rec = @()
          if(-not $okTls){ $rec += "Set RequireTLS `$true` on the send connector." }
          if(-not $okEop){ $rec += "Point to EOP smart hosts (*.mail.protection.outlook.com) or per your design." }
          Add-Row "SendConnector" $s.Name $status "RequireTLS: $($s.RequireTLS); SmartHosts: $hosts; Fqdn: $($s.Fqdn)" ($rec -join " ")
        }
      } else {
        Add-Row "SendConnector" "Outbound to EXO" (WARN) "No TLS-enabled EXO send connector detected" "Re-run HCW to recreate it."
      }
    } catch {
      Add-Row "SendConnector" "Query" (FAIL) $_.Exception.Message "Re-run HCW if needed."
    }
    
    # 5) Exchange Online connectors (optional)
    if($CheckEXO){
      try{
        if(-not (Get-Command Get-InboundConnector -ErrorAction SilentlyContinue)){
          throw "Not connected to Exchange Online. Run Connect-ExchangeOnline first."
        }
        $inb = Get-InboundConnector
        foreach($i in $inb){
          $tlsOK = $i.RequireTls -eq $true -and $null -ne $i.TlsSenderCertificateName
          $intOK = $i.TreatMessagesAsInternal
          $status = if($tlsOK -and $intOK){ OK } else { WARN }
          $rec = @()
          if(-not $i.RequireTls){ $rec += "Set RequireTLS `$true` on EXO Inbound connector." }
          if(-not $i.TlsSenderCertificateName){ $rec += "Set TlsSenderCertificateName to your SMTP cert CN/SAN." }
          if(-not $intOK){ $rec += "Set TreatMessagesAsInternal `$true` for hybrid." }
          Add-Row "EXO" "InboundConnector '$($i.Name)'" $status "RequireTLS: $($i.RequireTls); TlsSenderCertificateName: $($i.TlsSenderCertificateName); Internal:$intOK" ($rec -join " ")
        }
        $outb = Get-OutboundConnector
        foreach($o in $outb){
          $status = if($o.RequireTls){ OK } else { WARN }
          Add-Row "EXO" "OutboundConnector '$($o.Name)'" $status "RequireTLS: $($o.RequireTls); Domains: $($o.RecipientDomains -join ',')" (if($status -eq "WARN"){"Set RequireTLS `$true`."})
        }
      } catch {
        Add-Row "EXO" "Connector checks" (WARN) $_.Exception.Message "Connect-ExchangeOnline and retry."
      }
    }
    
    # 6) Optional remediation: fix TLS binding on Default Frontend
    if($FixTlsBinding){
      try{
        $active = Get-ExchangeCertificate -Server $ExchangeServer | Where-Object { $_.Services -match 'SMTP' -and -not $_.IsSelfSigned } | Sort-Object NotAfter -Descending | Select-Object -First 1
        if(-not $active){ throw "No suitable SMTP third-party certificate found." }
        $tlsName = "<I>$($active.Issuer)<S>$($active.Subject)"
        $def = Get-ReceiveConnector -Server $ExchangeServer | Where-Object { $_.Name -like "Default Frontend*" } | Select-Object -First 1
        if(-not $def){ throw "Default Frontend receive connector not found on $ExchangeServer." }
    
        if($PSCmdlet.ShouldProcess($def.Identity, "Bind TLS certificate $($active.Thumbprint.Substring(0,8))")){
          Set-ReceiveConnector $def.Identity -TlsCertificateName $tlsName
          Restart-Service MSExchangeFrontEndTransport -ErrorAction Stop
          Add-Row "Remediation" "Bind TLS to Default Frontend" (OK) "Set -TlsCertificateName and restarted FrontEndTransport" "Re-probe port 25 for STARTTLS."
        }
      } catch {
        Add-Row "Remediation" "Bind TLS to Default Frontend" (FAIL) $_.Exception.Message "Check cert/services and permissions."
      }
    }
    
    # Output
    $Report | Sort-Object Area,Item | Format-Table -AutoSize
    $Report

     

  • common causes for STARTTLS errors

     1. Missing or Incorrect TLS Certificate Binding

    • After renewing an SMTP certificate, the Receive Connector may still reference the old certificate in its TlsCertificateName property.
    • If the connector isn’t bound to a valid certificate, Exchange won’t advertise STARTTLS.

    ✅ 2. Self-Signed or Untrusted Certificate

    • If the certificate used for SMTP isn’t trusted by the sending system (or is self-signed), STARTTLS negotiation can fail.

    ✅ 3. STARTTLS Disabled on Receive Connector

    • The connector might have AuthMechanism settings that don’t include TLS, or RequireTLS is misconfigured.

    ✅ 4. Inline Devices Stripping STARTTLS

    • Firewalls, load balancers, or SMTP appliances can remove the STARTTLS capability from the SMTP banner.

    ✅ 5. DNS or FQDN Mismatch

    • The FQDN advertised by the connector doesn’t match the certificate CN/SAN, causing TLS handshake failures.

    ✅ 6. Outbound Connector Misconfiguration

    • On-prem or Exchange Online connectors not set to RequireTLS or missing TlsSenderCertificateName.

    ✅ 7. Expired Certificate

    • If the SMTP certificate is expired, STARTTLS will fail.

    ✅ 8. Opportunistic TLS Disabled

    • If the sending system doesn’t attempt STARTTLS and the receiving system requires it, the message will be rejected.
    • StefanoC66's avatar
      StefanoC66
      Iron Contributor

      jovanimp​ 

      The receive connector is the same for port 25 and 587

      below an extract of it

       

       

      unspaceId                                : 8694c07f-0c4d-40e5-848b-af31a34b85fd
      AuthMechanism                             : Tls, Integrated, BasicAuth, BasicAuthRequireTLS, ExchangeServer
      Banner                                    :
      BinaryMimeEnabled                         : True
      Bindings                                  : {[::]:25, 0.0.0.0:25}
      ChunkingEnabled                           : True
      DefaultDomain                             :
      DeliveryStatusNotificationEnabled         : True
      EightBitMimeEnabled                       : True
      SmtpUtf8Enabled                           : True
      BareLinefeedRejectionEnabled              : False
      DomainSecureEnabled                       : True
      EnhancedStatusCodesEnabled                : True
      LongAddressesEnabled                      : False
      OrarEnabled                               : False
      SuppressXAnonymousTls                     : False
      ProxyEnabled                              : False
      AdvertiseClientSettings                   : False
      Fqdn                                      : SRVEX01.domain.local
      ServiceDiscoveryFqdn                      :
      TlsCertificateName                        : <I>CN=DigiCert Global G2 TLS RSA SHA256 2020 CA1, O=DigiCert Inc, C=US<S>CN=*.XXXXX.com, O=XXXX

      Since the fact the connector is the same for the two ports but only on port 587 I see the STARTLS option makes me wonder about some particular setting to change.

      Looking on another installation ( exchange 2019 ) the startls is present also on port 25

      • William_Holmes's avatar
        William_Holmes
        Brass Contributor

        What is the output of Get-ExchangeCertificate on your on-premises server instance(s)?   Take a look at:

        https://learn.microsoft.com/en-us/powershell/module/exchangepowershell/enable-exchangecertificate?view=exchange-ps

        and make sure you enabled the correct valid cert for smtp.

Resources