diff --git a/migration/1566733587544-NoteSensitive.ts b/migration/1566733587544-NoteSensitive.ts
new file mode 100644
index 0000000000..cd249c5f31
--- /dev/null
+++ b/migration/1566733587544-NoteSensitive.ts
@@ -0,0 +1,13 @@
+import {MigrationInterface, QueryRunner} from "typeorm";
+
+export class NoteSensitive1566733587544 implements MigrationInterface {
+
+    public async up(queryRunner: QueryRunner): Promise<any> {
+        await queryRunner.query(`ALTER TABLE "note" ADD "isSensitive" boolean NOT NULL DEFAULT false`);
+    }
+
+    public async down(queryRunner: QueryRunner): Promise<any> {
+        await queryRunner.query(`ALTER TABLE "note" DROP COLUMN "isSensitive"`);
+    }
+
+}
diff --git a/src/models/entities/note.ts b/src/models/entities/note.ts
index 33a6d2bc27..a022d8ea1a 100644
--- a/src/models/entities/note.ts
+++ b/src/models/entities/note.ts
@@ -83,6 +83,12 @@ export class Note {
 	@JoinColumn()
 	public user: User | null;
 
+	@Column('boolean', {
+		default: false,
+		comment: 'Whether the Note is NSFW.'
+	})
+	public isSensitive: boolean;
+
 	@Column('boolean', {
 		default: false
 	})
diff --git a/src/remote/activitypub/renderer/note.ts b/src/remote/activitypub/renderer/note.ts
index cbdff7d203..8ccf009626 100644
--- a/src/remote/activitypub/renderer/note.ts
+++ b/src/remote/activitypub/renderer/note.ts
@@ -158,7 +158,7 @@ export default async function renderNote(note: Note, dive = true): Promise<any>
 		cc,
 		inReplyTo,
 		attachment: files.map(renderDocument),
-		sensitive: files.some(file => file.isSensitive),
+		sensitive: note.isSensitive || files.some(file => file.isSensitive),
 		tag,
 		...asPoll
 	};
diff --git a/src/server/api/endpoints/notes/local-timeline.ts b/src/server/api/endpoints/notes/local-timeline.ts
index c688b9325e..b42a7a9473 100644
--- a/src/server/api/endpoints/notes/local-timeline.ts
+++ b/src/server/api/endpoints/notes/local-timeline.ts
@@ -36,7 +36,7 @@ export const meta = {
 			validator: $.optional.bool,
 			default: false,
 			desc: {
-				'ja-JP': 'true にすると、NSFW指定されたファイルを除外します(fileTypeが指定されている場合のみ有効)'
+				'ja-JP': 'true にすると、NSFW指定されたコンテンツを除外します'
 			}
 		},
 
@@ -110,15 +110,10 @@ export default define(meta, async (ps, user) => {
 				qb.orWhere(`:type${i} = ANY(note.attachedFileTypes)`, { [`type${i}`]: type });
 			}
 		}));
+	}
 
-		if (ps.excludeNsfw) {
-			// v11 TODO
-			/*
-			query['_files.isSensitive'] = {
-				$ne: true
-			};
-			*/
-		}
+	if (ps.excludeNsfw) {
+		query.andWhere('note.isSensitive = FALSE');
 	}
 	//#endregion
 
diff --git a/src/server/api/endpoints/users/notes.ts b/src/server/api/endpoints/users/notes.ts
index ef564b3c34..4bc2c9810a 100644
--- a/src/server/api/endpoints/users/notes.ts
+++ b/src/server/api/endpoints/users/notes.ts
@@ -113,7 +113,7 @@ export const meta = {
 			validator: $.optional.bool,
 			default: false,
 			desc: {
-				'ja-JP': 'true にすると、NSFW指定されたファイルを除外します(fileTypeが指定されている場合のみ有効)'
+				'ja-JP': 'true にすると、NSFW指定されたコンテンツを除外します'
 			}
 		},
 	},
@@ -164,13 +164,10 @@ export default define(meta, async (ps, me) => {
 				qb.orWhere(`:type${i} = ANY(note.attachedFileTypes)`, { [`type${i}`]: type });
 			}
 		}));
+	}
 
-		if (ps.excludeNsfw) {
-			// v11 TODO
-			/*query['_files.isSensitive'] = {
-				$ne: true
-			};*/
-		}
+	if (ps.excludeNsfw) {
+		query.andWhere('note.isSensitive = FALSE');
 	}
 
 	if (!ps.includeReplies) {
diff --git a/src/services/note/create.ts b/src/services/note/create.ts
index 0dd0e1962f..7cc36b26bb 100644
--- a/src/services/note/create.ts
+++ b/src/services/note/create.ts
@@ -357,6 +357,7 @@ async function insertNote(user: User, data: Option, tags: string[], emojis: stri
 		tags: tags.map(tag => tag.toLowerCase()),
 		emojis,
 		userId: user.id,
+		isSensitive: data.cw != null || (data.files ? data.files.some(file => file.isSensitive) : false),
 		viaMobile: data.viaMobile!,
 		localOnly: data.localOnly!,
 		geo: data.geo || null,