diff --git a/core/src/config.rs b/core/src/config.rs
index 2db195b..be8ec27 100644
--- a/core/src/config.rs
+++ b/core/src/config.rs
@@ -139,6 +139,13 @@ pub struct CompatibilityConfig {
 	#[serde_inline_default(true)]
 	/// compatibility with lemmy: avoid showing images twice
 	pub skip_single_attachment_if_image_is_set: bool,
+
+	#[serde_inline_default(false)]
+	/// compatibility with most relays: since they send us other server's activities, we must fetch
+	/// them to verify that they aren't falsified by the relay itself. this is quite expensive, as
+	/// relays send a lot of activities and we effectively end up fetching again all these, so this
+	/// defaults to false
+	pub verify_relayed_activities_by_fetching: bool,
 }
 
 #[serde_inline_default::serde_inline_default]
diff --git a/routes/src/activitypub/inbox.rs b/routes/src/activitypub/inbox.rs
index 4253d1f..4666203 100644
--- a/routes/src/activitypub/inbox.rs
+++ b/routes/src/activitypub/inbox.rs
@@ -1,7 +1,7 @@
 use apb::{Activity, ActivityType, Base};
 use axum::{extract::{Query, State}, http::StatusCode, Json};
 use sea_orm::{sea_query::IntoCondition, ActiveValue::{NotSet, Set}, ColumnTrait, EntityTrait, QueryFilter, QueryOrder, QuerySelect};
-use upub::{model::job::JobType, selector::{RichActivity, RichFillable}, Context};
+use upub::{model::job::JobType, selector::{RichActivity, RichFillable}, traits::Fetcher, Context};
 
 use crate::{AuthIdentity, Identity, builders::JsonLD};
 
@@ -41,7 +41,7 @@ pub async fn page(
 pub async fn post(
 	State(ctx): State<Context>,
 	AuthIdentity(auth): AuthIdentity,
-	Json(activity): Json<serde_json::Value>
+	Json(mut activity): Json<serde_json::Value>
 ) -> crate::ApiResult<StatusCode> {
 	let Identity::Remote { domain, user: uid, .. } = auth else {
 		if matches!(activity.activity_type(), Ok(ActivityType::Delete)) {
@@ -72,7 +72,11 @@ pub async fn post(
 	let server = upub::Context::server(&aid);
 
 	if activity.actor().id()? != uid {
-		return Err(crate::ApiError::forbidden());
+		if ctx.cfg().compat.verify_relayed_activities_by_fetching {
+			activity = ctx.pull(&activity.id()?).await?.activity()?;
+		} else {
+			return Err(crate::ApiError::forbidden());
+		}
 	}
 
 	if let Some(_internal) = upub::model::activity::Entity::ap_to_internal(&aid, ctx.db()).await? {