diff --git a/include/asterisk/bridge_channel.h b/include/asterisk/bridge_channel.h
index 66f9be6e5fabd4353a3a5ddd99efdd0902bb1b61..797be4ebcb1ee13fb59aa3e2851773a0b75374a5 100644
--- a/include/asterisk/bridge_channel.h
+++ b/include/asterisk/bridge_channel.h
@@ -162,6 +162,8 @@ struct ast_bridge_channel {
 		struct timeval dtmf_tv;
 		/*! Digit currently sending into the bridge. (zero if not sending) */
 		char dtmf_digit;
+		/*! Non-zero if a T.38 session terminate is owed to the bridge. */
+		char t38_terminate;
 	} owed;
 	/*! DTMF hook sequence state */
 	struct {
diff --git a/main/bridge_channel.c b/main/bridge_channel.c
index 3f141452e500810c308fa79da43749d106bc374b..8172660335692563aa7ee3d3ee6fedfb7b5beed3 100644
--- a/main/bridge_channel.c
+++ b/main/bridge_channel.c
@@ -637,6 +637,8 @@ void ast_bridge_channel_kick(struct ast_bridge_channel *bridge_channel, int caus
  */
 static int bridge_channel_write_frame(struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
 {
+	const struct ast_control_t38_parameters *t38_parameters;
+
 	ast_assert(frame->frametype != AST_FRAME_BRIDGE_ACTION_SYNC);
 
 	ast_bridge_channel_lock_bridge(bridge_channel);
@@ -663,6 +665,27 @@ static int bridge_channel_write_frame(struct ast_bridge_channel *bridge_channel,
 		 * We explicitly will not remember HOLD/UNHOLD frames because
 		 * things like attended transfers will handle them.
 		 */
+		switch (frame->subclass.integer) {
+		case AST_CONTROL_T38_PARAMETERS:
+			t38_parameters = frame->data.ptr;
+			switch (t38_parameters->request_response) {
+			case AST_T38_REQUEST_NEGOTIATE:
+			case AST_T38_NEGOTIATED:
+				bridge_channel->owed.t38_terminate = 1;
+				break;
+			case AST_T38_REQUEST_TERMINATE:
+			case AST_T38_TERMINATED:
+			case AST_T38_REFUSED:
+				bridge_channel->owed.t38_terminate = 0;
+				break;
+			default:
+				break;
+			}
+			break;
+		default:
+			break;
+		}
+		break;
 	default:
 		break;
 	}
@@ -689,6 +712,7 @@ static int bridge_channel_write_frame(struct ast_bridge_channel *bridge_channel,
 static void bridge_channel_cancel_owed_events(struct ast_bridge_channel *bridge_channel)
 {
 	bridge_channel->owed.dtmf_digit = '\0';
+	bridge_channel->owed.t38_terminate = 0;
 }
 
 void bridge_channel_settle_owed_events(struct ast_bridge *orig_bridge, struct ast_bridge_channel *bridge_channel)
@@ -710,6 +734,23 @@ void bridge_channel_settle_owed_events(struct ast_bridge *orig_bridge, struct as
 		bridge_channel->owed.dtmf_digit = '\0';
 		orig_bridge->technology->write(orig_bridge, NULL, &frame);
 	}
+	if (bridge_channel->owed.t38_terminate) {
+		struct ast_control_t38_parameters t38_parameters = {
+			.request_response = AST_T38_TERMINATED,
+		};
+		struct ast_frame frame = {
+			.frametype = AST_FRAME_CONTROL,
+			.subclass.integer = AST_CONTROL_T38_PARAMETERS,
+			.data.ptr = &t38_parameters,
+			.datalen = sizeof(t38_parameters),
+			.src = "Bridge channel owed T.38 terminate",
+		};
+
+		ast_debug(1, "T.38 terminate simulated to bridge %s because %s left.\n",
+			orig_bridge->uniqueid, ast_channel_name(bridge_channel->chan));
+		bridge_channel->owed.t38_terminate = 0;
+		orig_bridge->technology->write(orig_bridge, NULL, &frame);
+	}
 }
 
 /*!
@@ -2736,6 +2777,18 @@ int bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel,
 			ast_channel_sending_dtmf_tv(bridge_channel->chan), "bridge end");
 	}
 
+	/* Complete any T.38 session before exiting the bridge. */
+	if (ast_channel_is_t38_active(bridge_channel->chan)) {
+		struct ast_control_t38_parameters t38_parameters = {
+			.request_response = AST_T38_TERMINATED,
+		};
+
+		ast_debug(1, "Channel %s simulating T.38 terminate for bridge end.\n",
+			ast_channel_name(bridge_channel->chan));
+		ast_indicate_data(bridge_channel->chan, AST_CONTROL_T38_PARAMETERS,
+			&t38_parameters, sizeof(t38_parameters));
+	}
+
 	/* Indicate a source change since this channel is leaving the bridge system. */
 	ast_indicate(bridge_channel->chan, AST_CONTROL_SRCCHANGE);
 
diff --git a/main/channel.c b/main/channel.c
index f9addb01625f88fb5afd5ed52e60197417799b3c..852de1aef8f358d41984bee849c7044ca8604dc0 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -4479,6 +4479,7 @@ static int indicate_data_internal(struct ast_channel *chan, int _condition, cons
 	 * in switch statements. */
 	enum ast_control_frame_type condition = _condition;
 	struct ast_tone_zone_sound *ts = NULL;
+	const struct ast_control_t38_parameters *t38_parameters;
 	int res;
 
 	switch (condition) {
@@ -4498,6 +4499,22 @@ static int indicate_data_internal(struct ast_channel *chan, int _condition, cons
 	case AST_CONTROL_UNHOLD:
 		ast_channel_hold_state_set(chan, _condition);
 		break;
+	case AST_CONTROL_T38_PARAMETERS:
+		t38_parameters = data;
+		switch (t38_parameters->request_response) {
+		case AST_T38_REQUEST_NEGOTIATE:
+		case AST_T38_NEGOTIATED:
+			ast_channel_set_is_t38_active_nolock(chan, 1);
+			break;
+		case AST_T38_REQUEST_TERMINATE:
+		case AST_T38_TERMINATED:
+		case AST_T38_REFUSED:
+			ast_channel_set_is_t38_active_nolock(chan, 0);
+			break;
+		default:
+			break;
+		}
+		break;
 	default:
 		break;
 	}